7.2.9-rc1 review
Subsystems (6)
Greg Kroah-Hartman
maintainerPATCH 122/457 ovpn: always unhash old VPN addresses before rehashing
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ralf Lici <[email protected]>
[ Upstream commit b43beccb3713fafada57814b0a652f4a876eb75f ]
ovpn_peer_hash_vpn_ip updates the per-peer VPN address hash entries after userspace changes a peer VPN address. The current code removes an old hash entry only when the new address for that family is not the unspecified address.
When an address is cleared to 0.0.0.0 or ::, its hash node therefore remains linked in the bucket selected by the old address. The address comparison performed during lookup prevents the old address from matching, but the table retains a stale entry until the peer is removed or another address is configured for that family.
Always remove both old VPN address hash entries before conditionally adding the currently configured addresses back. This ensures that a cleared address leaves its hash node unhashed.
--- drivers/net/ovpn/peer.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-)
drivers/net/ovpn/peer.c +4 -6
@@ -994,10 +994,11 @@ void ovpn_peer_hash_vpn_ip(struct ovpn_peer *peer)if (hlist_unhashed (&peer->hash_entry_id))return ; -if (peer->vpn_addrs.ipv4.s_addr !=htonl (INADDR_ANY )) { -/* remove potential old hashing */ -hlist_nulls_del_init_rcu (&peer->hash_entry_addr4); +/* remove potential old hashing */ +hlist_nulls_del_init_rcu (&peer->hash_entry_addr4); +hlist_nulls_del_init_rcu (&peer->hash_entry_addr6); +if (peer->vpn_addrs.ipv4.s_addr !=htonl (INADDR_ANY )) { nhead =ovpn_get_hash_head (peer->ovpn->peers->by_vpn_addr4, &peer->vpn_addrs.ipv4,sizeof (peer->vpn_addrs.ipv4));
@@ -1005,9 +1006,6 @@ void ovpn_peer_hash_vpn_ip(struct ovpn_peer *peer) }if (!ipv6_addr_any (&peer->vpn_addrs.ipv6)) { -/* remove potential old hashing */ -hlist_nulls_del_init_rcu (&peer->hash_entry_addr6); - nhead =ovpn_get_hash_head (peer->ovpn->peers->by_vpn_addr6, &peer->vpn_addrs.ipv6,sizeof (peer->vpn_addrs.ipv6));
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 123/457 ovpn: reject duplicate peer VPN addresses
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ralf Lici <[email protected]>
[ Upstream commit d25e885b31a0f2808d936f95c9a558a8a792669b ]
In MP mode, ovpn uses the peer VPN addresses as lookup keys for selecting the peer that should receive an outgoing tunnel packet. However, the netlink peer configuration path does not currently reject duplicate VPN addresses.
If two peers are configured with the same VPN address, both can be inserted in the VPN address hash table and lookups return whichever peer is found first. This makes peer selection ambiguous and dependent on hash insertion order.
Reject peer creation or update when the resulting VPN address is already assigned to another peer. Ignore unspecified addresses because those are not inserted in the VPN address hash tables.
This changes such configurations from being accepted to being rejected, but they have never worked reliably because peer selection is ambiguous.
--- drivers/net/ovpn/netlink.c | 37 ++++++++++++++++----- drivers/net/ovpn/peer.c | 67 +++++++++++++++++++++++++++++++++++++- drivers/net/ovpn/peer.h | 6 ++++ 3 files changed, 101 insertions(+), 9 deletions(-)
drivers/net/ovpn/netlink.c +29 -8
@@ -480,8 +480,10 @@ int ovpn_nl_peer_new_doit(struct sk_buff *skb, struct genl_info *info)int ovpn_nl_peer_set_doit (struct sk_buff *skb,struct genl_info *info) { -struct nlattr *attrs[OVPN_A_PEER_MAX +1 ];struct ovpn_priv *ovpn = info->user_ptr[0 ]; +struct nlattr *attrs[OVPN_A_PEER_MAX +1 ]; +struct in6_addr vpn_addr6; +struct in_addr vpn_addr4;struct ovpn_socket *sock;struct ovpn_peer *peer;u32 peer_id;
@@ -528,28 +530,47 @@ int ovpn_nl_peer_set_doit(struct sk_buff *skb, struct genl_info *info)rcu_read_unlock ();spin_lock_bh (&ovpn->lock); - ret =ovpn_nl_peer_modify (peer, info, attrs); -if (ret <0 ) { -spin_unlock_bh (&ovpn->lock); -ovpn_peer_put (peer); -return ret; + +/* reject peer with conflicting VPN address */ +if (attrs[OVPN_A_PEER_VPN_IPV4 ]) { + vpn_addr4.s_addr =nla_get_in_addr (attrs[OVPN_A_PEER_VPN_IPV4 ]); +if (ovpn_peer_vpn_addr_conflict4 (ovpn, peer, &vpn_addr4)) + goto addr_conflict; } +if (attrs[OVPN_A_PEER_VPN_IPV6 ]) { + vpn_addr6 =nla_get_in6_addr (attrs[OVPN_A_PEER_VPN_IPV6 ]); +if (ovpn_peer_vpn_addr_conflict6 (ovpn, peer, &vpn_addr6)) + goto addr_conflict; + } + + ret =ovpn_nl_peer_modify (peer, info, attrs); +if (ret <0 ) + goto unlock;/* ret == 1 means that VPN IPv4/6 has been modified and rehashing * is required */ -if (ret >0 ) +if (ret >0 ) {ovpn_peer_hash_vpn_ip (peer); + ret =0 ; + }/* if the remote endpoint was updated, the by_transp_addr hash bucket * also needs to be refreshed, otherwise incoming packets from the new * remote address would fail the lockless lookup */ if (attrs[OVPN_A_PEER_REMOTE_IPV4 ] || attrs[OVPN_A_PEER_REMOTE_IPV6 ])ovpn_peer_hash_transp_addr (peer); + +unlock:spin_unlock_bh (&ovpn->lock);ovpn_peer_put (peer); -return 0 ; +return ret; +addr_conflict: +NL_SET_ERR_MSG_FMT_MOD (info->extack, +"VPN IP is already assigned to another peer" ); + ret = -EADDRINUSE ; + goto unlock; } static int ovpn_nl_send_peer(struct sk_buff *skb, const struct genl_info *info,
drivers/net/ovpn/peer.c +66 -1
85 lines not on this page · raw message
drivers/net/ovpn/peer.h +6 -0
@@ -164,6 +164,12 @@ struct ovpn_peer *ovpn_peer_get_by_transp_addr(struct ovpn_priv *ovpn,struct ovpn_peer *ovpn_peer_get_by_id (struct ovpn_priv *ovpn,u32 peer_id);struct ovpn_peer *ovpn_peer_get_by_dst (struct ovpn_priv *ovpn,struct sk_buff *skb); +bool ovpn_peer_vpn_addr_conflict4 (struct ovpn_priv *ovpn, +const struct ovpn_peer *peer, +const struct in_addr *addr); +bool ovpn_peer_vpn_addr_conflict6 (struct ovpn_priv *ovpn, +const struct ovpn_peer *peer, +const struct in6_addr *addr);void ovpn_peer_hash_vpn_ip (struct ovpn_peer *peer);void ovpn_peer_hash_transp_addr (struct ovpn_peer *peer); bool ovpn_peer_check_by_src(struct ovpn_priv *ovpn, struct sk_buff *skb,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 124/457 ovpn: reject multipeer peers without VPN addresses
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ralf Lici <[email protected]>
[ Upstream commit 025af3a0a892514f9f27f186338ba3d44365547a ]
In MP mode, ovpn uses the peer VPN addresses to select the peer for outgoing tunnel packets. Peer creation currently requires a VPN IPv4 or IPv6 attribute, but it only checks for the presence of the attribute and not for a usable address value.
This allows userspace to create an MP peer with only unspecified VPN addresses, or to update an existing peer so that both VPN address families become unspecified. Such a peer cannot be selected through the VPN address hash tables.
Reject MP peer creation or update when the resulting peer would not have at least one VPN address configured.
This changes such configurations from being accepted to being rejected, but they have never been usable because the peer cannot be selected through the VPN address hash tables.
--- drivers/net/ovpn/netlink.c | 36 ++++++++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-)
drivers/net/ovpn/netlink.c +30 -6
@@ -352,8 +352,10 @@ static int ovpn_nl_peer_modify(struct ovpn_peer *peer, struct genl_info *info,int ovpn_nl_peer_new_doit (struct sk_buff *skb,struct genl_info *info) { -struct nlattr *attrs[OVPN_A_PEER_MAX +1 ]; +struct in_addr vpn_addr4 = { .s_addr =htonl (INADDR_ANY ) }; +struct in6_addr vpn_addr6 =IN6ADDR_ANY_INIT ;struct ovpn_priv *ovpn = info->user_ptr[0 ]; +struct nlattr *attrs[OVPN_A_PEER_MAX +1 ];struct ovpn_socket *ovpn_sock;struct socket *sock =NULL ;struct ovpn_peer *peer;
@@ -377,11 +379,20 @@ int ovpn_nl_peer_new_doit(struct sk_buff *skb, struct genl_info *info)return -EINVAL ;/* in MP mode VPN IPs are required for selecting the right peer */ -if (ovpn->mode ==OVPN_MODE_MP && !attrs[OVPN_A_PEER_VPN_IPV4 ] && - !attrs[OVPN_A_PEER_VPN_IPV6 ]) { -NL_SET_ERR_MSG_FMT_MOD (info->extack, -"VPN IP must be provided in MP mode" ); -return -EINVAL ; +if (ovpn->mode ==OVPN_MODE_MP ) { +if (attrs[OVPN_A_PEER_VPN_IPV4 ]) + vpn_addr4.s_addr = +nla_get_in_addr (attrs[OVPN_A_PEER_VPN_IPV4 ]); +if (attrs[OVPN_A_PEER_VPN_IPV6 ]) + vpn_addr6 = +nla_get_in6_addr (attrs[OVPN_A_PEER_VPN_IPV6 ]); + +if (vpn_addr4.s_addr ==htonl (INADDR_ANY ) && +ipv6_addr_any (&vpn_addr6)) { +NL_SET_ERR_MSG_FMT_MOD (info->extack, +"at least one VPN IP must be configured in MP mode" ); +return -EINVAL ; + } } peer_id =nla_get_u32 (attrs[OVPN_A_PEER_ID ]);
@@ -531,6 +542,9 @@ int ovpn_nl_peer_set_doit(struct sk_buff *skb, struct genl_info *info)spin_lock_bh (&ovpn->lock); + vpn_addr4 = peer->vpn_addrs.ipv4; + vpn_addr6 = peer->vpn_addrs.ipv6; +/* reject peer with conflicting VPN address */ if (attrs[OVPN_A_PEER_VPN_IPV4 ]) { vpn_addr4.s_addr =nla_get_in_addr (attrs[OVPN_A_PEER_VPN_IPV4 ]);
@@ -543,6 +557,16 @@ int ovpn_nl_peer_set_doit(struct sk_buff *skb, struct genl_info *info) goto addr_conflict; } +/* in MP mode VPN IPs are required for selecting the right peer */ +if (ovpn->mode ==OVPN_MODE_MP && + vpn_addr4.s_addr ==htonl (INADDR_ANY ) && +ipv6_addr_any (&vpn_addr6)) { +NL_SET_ERR_MSG_FMT_MOD (info->extack, +"at least one VPN IP must be configured in MP mode" ); + ret = -EINVAL ; + goto unlock; + } + ret =ovpn_nl_peer_modify (peer, info, attrs);if (ret <0 ) goto unlock;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 125/457 ovpn: reject invalid peer VPN addresses
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ralf Lici <[email protected]>
[ Upstream commit 5940f3407b78062442cb01f541ef6eed709fc380 ]
In MP mode, ovpn uses peer VPN addresses as lookup keys for selecting the peer that should receive outgoing tunnel packets. The netlink configuration path currently accepts address values that cannot sensibly identify a VPN peer, such as multicast, broadcast or loopback addresses.
Reject invalid peer VPN addresses when creating or updating an MP peer. Keep accepting the unspecified address as the internal unset value, provided that at least one VPN address family remains configured.
--- drivers/net/ovpn/netlink.c | 55 +++++++++++++++++++++++++++++--------- 1 file changed, 42 insertions(+), 13 deletions(-)
drivers/net/ovpn/netlink.c +42 -13
@@ -185,6 +185,39 @@ static sa_family_t ovpn_nl_family_get(struct nlattr *addr4,return AF_UNSPEC ; } +static int ovpn_nl_peer_check_vpn_addrs (const struct in_addr *addr4, +const struct in6_addr *addr6, +struct genl_info *info) +{ +int addr6_type; + +if (addr4->s_addr ==htonl (INADDR_ANY ) &&ipv6_addr_any (addr6)) { +NL_SET_ERR_MSG_MOD (info->extack, +"at least one VPN IP must be configured in MP mode" ); +return -EINVAL ; + } + +if (ipv4_is_multicast (addr4->s_addr) ||ipv4_is_lbcast (addr4->s_addr) || +ipv4_is_loopback (addr4->s_addr)) { +NL_SET_ERR_MSG_MOD (info->extack, +"VPN IPv4 address must be valid unicast or any" ); +return -EADDRNOTAVAIL ; + } + +if (!ipv6_addr_any (addr6)) { + addr6_type =ipv6_addr_type (addr6); + +if (!(addr6_type &IPV6_ADDR_UNICAST ) || + (addr6_type & (IPV6_ADDR_LOOPBACK | IPV6_ADDR_COMPATv4))) { +NL_SET_ERR_MSG_MOD (info->extack, +"VPN IPv6 address must be valid unicast or any" ); +return -EADDRNOTAVAIL ; + } + } + +return 0 ; +} +static int ovpn_nl_peer_precheck (struct ovpn_priv *ovpn,struct genl_info *info,struct nlattr **attrs)
@@ -387,12 +420,10 @@ int ovpn_nl_peer_new_doit(struct sk_buff *skb, struct genl_info *info) vpn_addr6 =nla_get_in6_addr (attrs[OVPN_A_PEER_VPN_IPV6 ]); -if (vpn_addr4.s_addr ==htonl (INADDR_ANY ) && -ipv6_addr_any (&vpn_addr6)) { -NL_SET_ERR_MSG_FMT_MOD (info->extack, -"at least one VPN IP must be configured in MP mode" ); -return -EINVAL ; - } + ret =ovpn_nl_peer_check_vpn_addrs (&vpn_addr4, &vpn_addr6, + info); +if (ret <0 ) +return ret; } peer_id =nla_get_u32 (attrs[OVPN_A_PEER_ID ]);
@@ -558,13 +589,11 @@ int ovpn_nl_peer_set_doit(struct sk_buff *skb, struct genl_info *info) }/* in MP mode VPN IPs are required for selecting the right peer */ -if (ovpn->mode ==OVPN_MODE_MP && - vpn_addr4.s_addr ==htonl (INADDR_ANY ) && -ipv6_addr_any (&vpn_addr6)) { -NL_SET_ERR_MSG_FMT_MOD (info->extack, -"at least one VPN IP must be configured in MP mode" ); - ret = -EINVAL ; - goto unlock; +if (ovpn->mode ==OVPN_MODE_MP ) { + ret =ovpn_nl_peer_check_vpn_addrs (&vpn_addr4, &vpn_addr6, + info); +if (ret <0 ) + goto unlock; } ret =ovpn_nl_peer_modify (peer, info, attrs);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 126/457 drm/xe/gt_throttle: Report power brake as a throttle reason on CRI
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sk Anirban <[email protected]>
[ Upstream commit ea4debcd8016f73c5dee3a29250a3d7977f015ef ]
CRI defines bit 5 of the perf limit reasons register as a power brake (PWRBRK) indicator. Add PWRBRK_MASK and a reason_pwrbrk sysfs attribute for CRI in place of reason_ratl.
--- drivers/gpu/drm/xe/regs/xe_gt_regs.h | 1 + drivers/gpu/drm/xe/xe_gt_throttle.c | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-)
drivers/gpu/drm/xe/regs/xe_gt_regs.h +1 -0
@@ -651,6 +651,7 @@#define MEM_THERMAL_MASK REG_BIT(2)#define VR_THERMAL_MASK REG_BIT(3)#define ICCMAX_MASK REG_BIT(4) +#define PWRBRK_MASK REG_BIT(5)#define SOC_AVG_THERMAL_MASK REG_BIT(6)#define FASTVMODE_MASK REG_BIT(7)#define PSYS_PL1_MASK REG_BIT(12)
drivers/gpu/drm/xe/xe_gt_throttle.c +3 -2
@@ -39,7 +39,7 @@* - ``reason_mem_thermal``: Memory thermal * - ``reason_vr_thermal``: VR thermal * - ``reason_iccmax``: ICCMAX -* - ``reason_ratl``: RATL thermal algorithm +* - ``reason_pwrbrk``: Power brake * - ``reason_soc_avg_thermal``: SoC average temp * - ``reason_fastvmode``: VR is hitting FastVMode * - ``reason_psys_pl1``: PSYS PL1
@@ -200,6 +200,7 @@ static THROTTLE_ATTR_RO(reason_psys_pl1, PSYS_PL1_MASK); static THROTTLE_ATTR_RO(reason_psys_pl2, PSYS_PL2_MASK); static THROTTLE_ATTR_RO(reason_p0_freq, P0_FREQ_MASK); static THROTTLE_ATTR_RO(reason_psys_crit, PSYS_CRIT_MASK); +static THROTTLE_ATTR_RO(reason_pwrbrk, PWRBRK_MASK);static struct attribute *cri_throttle_attrs[] = {/* Common */
@@ -209,12 +210,12 @@ static struct attribute *cri_throttle_attrs[] = { &attr_reason_pl2.attr.attr, &attr_reason_pl4.attr.attr, &attr_reason_prochot.attr.attr, - &attr_reason_ratl.attr.attr,/* CRI */ &attr_reason_vr_thermal.attr.attr, &attr_reason_soc_thermal.attr.attr, &attr_reason_mem_thermal.attr.attr, &attr_reason_iccmax.attr.attr, + &attr_reason_pwrbrk.attr.attr, &attr_reason_soc_avg_thermal.attr.attr, &attr_reason_fastvmode.attr.attr, &attr_reason_psys_pl1.attr.attr,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 127/457 Bluetooth: bnep: fix out-of-bounds reads on short RX/TX frames and control fallthrough
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hui Peng <[email protected]>
[ Upstream commit f0ca020cbb9bb7f3f4ea8ba1dfcf30a282aec91e ]
Fix multiple out-of-bounds reads in Bluetooth BNEP frame processing:
1. In bnep_rx_frame() and bnep_ctrl_frame() (net/bluetooth/bnep/core.c), use pskb_may_pull() to verify the BNEP header, control type byte, filter count, and extension headers exist before reading them, and return 0 after handling BNEP_CONTROL instead of falling through to Ethernet frame submission when no extension headers follow.
2. In bnep_net_xmit() (net/bluetooth/bnep/netdev.c), verify skb->len >= ETH_HLEN with pskb_may_pull() before reading the 14-byte Ethernet header to prevent an out-of-bounds heap read and infoleak on short AF_PACKET TX frames.
--- net/bluetooth/bnep/core.c | 17 ++++++++++++++++- net/bluetooth/bnep/netdev.c | 8 +++++++- 2 files changed, 23 insertions(+), 2 deletions(-)
net/bluetooth/bnep/core.c +16 -1
@@ -270,9 +270,14 @@ static int bnep_rx_extension(struct bnep_session *s, struct sk_buff *skb)BT_DBG ("type 0x%x len %u" , h->type, h->len); +if (skb->len < h->len) { + err = -EILSEQ ; +break ; + } +switch (h->type &BNEP_TYPE_MASK ) {case BNEP_EXT_CONTROL : -bnep_rx_control (s, skb->data, skb->len); +bnep_rx_control (s, skb->data, h->len);break ;default :
@@ -373,6 +378,11 @@ static int bnep_rx_frame(struct bnep_session *s, struct sk_buff *skb) goto badframe; } +if ((type &BNEP_TYPE_MASK ) ==BNEP_CONTROL ) { +kfree_skb (skb); +return 0 ; + } +/* Strip 802.1p header */ if (ntohs (s->eh.h_proto) ==ETH_P_8021Q ) {if (!skb_pull (skb,4 ))
@@ -451,6 +461,11 @@ static int bnep_tx_frame(struct bnep_session *s, struct sk_buff *skb) goto send; } +if (skb->len <ETH_HLEN ) { +kfree_skb (skb); +return 0 ; + } + iv[il++] = (struct kvec ) { &type,1 }; len++;
net/bluetooth/bnep/netdev.c +7 -1
@@ -166,6 +166,12 @@ static netdev_tx_t bnep_net_xmit(struct sk_buff *skb,BT_DBG ("skb %p, dev %p" , skb, dev); +if (!pskb_may_pull (skb,ETH_HLEN )) { + dev->stats.tx_dropped++; +kfree_skb (skb); +return NETDEV_TX_OK ; + } +#ifdef CONFIG_BT_BNEP_MC_FILTER if (bnep_net_mc_filter (skb, s)) {kfree_skb (skb);
@@ -218,7 +224,7 @@ void bnep_net_setup(struct net_device *dev) dev->addr_len =ETH_ALEN ;ether_setup (dev); - dev->min_mtu =0 ; + dev->min_mtu =ETH_MIN_MTU ; dev->max_mtu =ETH_MAX_MTU ; dev->priv_flags &= ~IFF_TX_SKB_SHARING ; dev->netdev_ops = &bnep_netdev_ops;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 128/457 Bluetooth: btintel_pcie: validate device-supplied DMA indices
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ravindra <[email protected]>
[ Upstream commit 37a11129345337efd6eef8e62b03b6348cd0dd8b ]
In btintel_pcie_msix_rx_handle(), the driver processes RX completion descriptors (urbd1) written by the PCIe device into DMA-coherent memory. urbd1->frbd_tag (a 16-bit field fully controlled by the device firmware via DMA) is used directly as an array index into rxq->bufs[] without any bounds check. rxq->bufs[] has only BTINTEL_PCIE_RX_DESCS_COUNT (64) entries, while frbd_tag can be any value 0-65535. A malicious or malfunctioning device can write an out-of-range frbd_tag, causing the driver to dereference an out-of-bounds data_buf pointer.
Additionally, cr_hia is read from a DMA-shared index array also writable by the device; if the device sets cr_hia >= rxq->count, the while-loop never terminates because cr_tia is wrapped via modulo rxq->count and can never equal an out-of-range cr_hia.
Add bounds validation for cr_hia and frbd_tag in the RX path, and cr_hia in the TX path. Log invalid values with bt_dev_err before returning.
--- drivers/bluetooth/btintel_pcie.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+)
drivers/bluetooth/btintel_pcie.c +16 -0
@@ -1099,6 +1099,11 @@ static void btintel_pcie_msix_tx_handle(struct btintel_pcie_data *data) txq = &data->txq; +if (cr_hia >= txq->count) { +bt_dev_err (data->hdev,"TXQ: invalid cr_hia %u" , cr_hia); +return ; + } +while (cr_tia != cr_hia) { data->tx_wait_done = true;wake_up (&data->tx_wait_q);
@@ -1588,6 +1593,11 @@ static void btintel_pcie_msix_rx_handle(struct btintel_pcie_data *data) rxq = &data->rxq; +if (cr_hia >= rxq->count) { +bt_dev_err (hdev,"RXQ: invalid cr_hia %u" , cr_hia); +return ; + } +/* The firmware sends multiple CD in a single MSI-X and it needs to * process all received CDs in this interrupt. */
@@ -1595,6 +1605,12 @@ static void btintel_pcie_msix_rx_handle(struct btintel_pcie_data *data) urbd1 = &rxq->urbd1s[cr_tia];ipc_print_urbd1 (data->hdev, urbd1, cr_tia); +if (urbd1->frbd_tag >= rxq->count) { +bt_dev_err (hdev,"RXQ: invalid frbd_tag %u" , + urbd1->frbd_tag); +return ; + } + buf = &rxq->bufs[urbd1->frbd_tag];if (!buf) { bt_dev_err(hdev, "RXQ: failed to get the DMA buffer for %d",
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 129/457 Bluetooth: RFCOMM: Reject short EA=0 frames in rfcomm_recv_frame()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hui Peng <[email protected]>
[ Upstream commit 6d91041bb38b97e2feb625123cc0529d7b83a0e1 ]
While rfcomm_recv_frame() verifies that skb->len is at least sizeof(*hdr) + 1 (4 bytes: 3-byte header + 1-byte FCS), an RFCOMM frame with an extended 2-byte length field (!__test_ea(hdr->len)) has a 4-byte header plus a 1-byte FCS (5 bytes minimum, sizeof(*hdr) + 2).
When a 4-byte RFCOMM frame with EA == 0 arrives:
1. The initial skb->len < sizeof(*hdr) + 1 check passes (4 < 4 is false).
2. Trimming the FCS byte decrements skb->len to 3.
3. If __check_fcs() succeeds, skb_pull(skb, 4) fails (4 > 3) and returns NULL without advancing skb->data.
4. Because the return value of skb_pull() is ignored, the un-pulled 3-byte struct rfcomm_hdr remains at skb->data and is either queued as application payload via rfcomm_recv_data() or parsed as a multiplexer control command via rfcomm_recv_mcc() on DLCI 0.
Fix this by extending the length check in rfcomm_recv_frame() to also require skb->len >= sizeof(*hdr) + 2 when !__test_ea(hdr->len).
--- net/bluetooth/rfcomm/core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
net/bluetooth/rfcomm/core.c +2 -1
@@ -1817,7 +1817,8 @@ static struct rfcomm_session *rfcomm_recv_frame(struct rfcomm_session *s,return s; } -if (skb->len <sizeof (*hdr) +1 ) { +if (skb->len <sizeof (*hdr) +1 || + (!__test_ea (hdr->len) && skb->len <sizeof (*hdr) +2 )) {kfree_skb (skb);return s; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 130/457 bpf: Reject non-negative offsets in stack_slot_obj_get_spi()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xu Yunxiang <[email protected]>
[ Upstream commit 79a9172f3ab4ad8392c5e5c8944b9b7710ade620 ]
bpf_get_spi() computes (-off - 1) / BPF_REG_SIZE using C division, which truncates toward zero. For off == 0, this produces spi 0, the same index used by the valid stack slot at fp-8.
stack_slot_obj_get_spi() currently checks alignment and the resulting spi bounds, but does not reject the non-negative offset itself. It can therefore validate a PTR_TO_STACK register holding fp+0 against an iterator stored at fp-8 even though the runtime receives the actual fp+0 pointer. An effectful iterator kfunc can then interpret memory outside the BPF stack as iterator state.
Reject non-negative offsets before converting the offset to an spi. All valid stack objects begin at a negative offset from the frame pointer.
--- kernel/bpf/verifier.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
kernel/bpf/verifier.c +1 -1
@@ -582,7 +582,7 @@ static int stack_slot_obj_get_spi(struct bpf_verifier_env *env, struct bpf_reg_s } off = reg->var_off.value; -if (off %BPF_REG_SIZE ) { +if (off >=0 || off %BPF_REG_SIZE ) {verbose (env,"cannot pass in %s at an offset=%d\n" , obj_kind, off);return -EINVAL ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 131/457 octeontx2-af: Fix memory scaling limitation in SR-IOV mode
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ratheesh Kannoth <[email protected]>
[ Upstream commit d06f2ebf67ff2962fe00d687e4f0d4703eb41a12 ]
The original code used DMA_ATTR_FORCE_CONTIGUOUS, which could exhaust the CMA pool when a large number of VFs were requested.
Fix this by switching to the DMA streaming API. This is equivalent on Octeon platforms, which provide full I/O coherency via the SMMU.
--- .../ethernet/marvell/octeontx2/af/common.h | 45 ++++++++++++++++--- 1 file changed, 39 insertions(+), 6 deletions(-)
drivers/net/ethernet/marvell/octeontx2/af/common.h +39 -6
@@ -7,6 +7,10 @@#ifndef COMMON_H #define COMMON_H +#include <linux/dma-mapping.h> +#include <linux/gfp.h> +#include <linux/mm.h> +#include "rvu_struct.h" #define OTX2_ALIGN 128/* Align to cacheline */
@@ -44,6 +48,33 @@ struct qmem {u32 qsize; }; +static inline void *otx2_dma_alloc_coherent (struct device *dev,size_t size, +dma_addr_t *dma_handle) +{ +dma_addr_t dma_addr; +void *vaddr; + + vaddr =kzalloc (size,GFP_KERNEL ); +if (!vaddr) +return NULL ; + + dma_addr =dma_map_single (dev, vaddr, size,DMA_BIDIRECTIONAL ); +if (dma_mapping_error (dev, dma_addr)) { +kfree (vaddr); +return NULL ; + } + + *dma_handle = dma_addr; +return vaddr; +} + +static inline void otx2_dma_free_coherent (struct device *dev,size_t size, +void *vaddr,dma_addr_t dma_handle) +{ +dma_unmap_single (dev, dma_handle, size,DMA_BIDIRECTIONAL ); +kfree (vaddr); +} +static inline int qmem_alloc (struct device *dev,struct qmem **q,int qsize,int entry_sz) {
@@ -60,8 +91,11 @@ static inline int qmem_alloc(struct device *dev, struct qmem **q, qmem->entry_sz = entry_sz; qmem->alloc_sz = (qsize * entry_sz) +OTX2_ALIGN ; - qmem->base =dma_alloc_attrs (dev, qmem->alloc_sz, &qmem->iova, -GFP_KERNEL ,DMA_ATTR_FORCE_CONTIGUOUS ); + +if (get_order (PAGE_ALIGN (qmem->alloc_sz)) >MAX_PAGE_ORDER ) +return -ENOMEM ; + + qmem->base =otx2_dma_alloc_coherent (dev, qmem->alloc_sz, &qmem->iova);if (!qmem->base)return -ENOMEM ;
@@ -80,10 +114,9 @@ static inline void qmem_free(struct device *dev, struct qmem *qmem)return ;if (qmem->base) -dma_free_attrs (dev, qmem->alloc_sz, - qmem->base - qmem->align, - qmem->iova - qmem->align, -DMA_ATTR_FORCE_CONTIGUOUS ); +otx2_dma_free_coherent (dev, qmem->alloc_sz, + qmem->base - qmem->align, + qmem->iova - qmem->align);devm_kfree (dev, qmem); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 132/457 ipv6: Prevent rt6_insert_exception() for dying fib6_info.
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kuniyuki Iwashima <[email protected]>
[ Upstream commit 0346ec2f080b40d95ed05b853bb9226289e75212 ]
Before the cited commit, fib6_nh_flush_exceptions() always set from->exception_bucket_flushed = 1 under rt6_exception_lock to prevent rt6_insert_exception() from inserting a new exception for a dying fib6_info.
The flag was replaced with the FIB6_EXCEPTION_BUCKET_FLUSHED bit stored in nh->rt6i_exception_bucket.
The problem is that now the bit is only set when the bucket is not NULL and fib6_nh_flush_exceptions() is called from fib6_nh_release() after fib6_ref has already reached zero.
If rt6_insert_exception() is called while the target fib6_info is being removed via fib6_purge_rt(), a new exception could be created successfully because rt6_flush_exceptions() no longer sets the bit.
This creates a reference cycle between the fib6_info and the exception route, leaking the fib6_info, its nexthop device, and all per-CPU routes in fib6_nh->rt6i_pcpu, which stalls netdev unregistration.
[ 34.680602] unregister_netdevice: waiting for gre6 to become free. Usage count = 68 [ 44.920675] unregister_netdevice: waiting for gre6 to become free. Usage count = 68 [ 55.176582] unregister_netdevice: waiting for gre6 to become free. Usage count = 68
Let's call fib6_drop_pcpu_from() before rt6_flush_exceptions(), to set fib6_destroying before rt6_exception_lock, and check f6i->fib6_destroying in rt6_insert_exception().
Note that FIB6_EXCEPTION_BUCKET_FLUSHED logic is dead and we can clean it up in net-next.
--- net/ipv6/ip6_fib.c | 2 +- net/ipv6/route.c | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-)
net/ipv6/ip6_fib.c +1 -1
@@ -1043,8 +1043,8 @@ static void fib6_purge_rt(struct fib6_info *rt, struct fib6_node *fn,struct fib6_table *table = rt->fib6_table;/* Flush all cached dst in exception table */ -rt6_flush_exceptions (rt);fib6_drop_pcpu_from (rt); +rt6_flush_exceptions (rt);if (rt->nh) {spin_lock (&rt->nh->lock);
net/ipv6/route.c +5 -0
@@ -1729,6 +1729,11 @@ static int rt6_insert_exception(struct rt6_info *nrt,spin_lock_bh (&rt6_exception_lock); +if (f6i->fib6_destroying) { + err = -ENOENT ; + goto out; + } + bucket =rcu_dereference_protected (nh->rt6i_exception_bucket,lockdep_is_held (&rt6_exception_lock));if (!bucket) {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 133/457 net: dont require the hwtstamp NDOs when a PHY provides timestamping
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Nicolai Buchwitz <[email protected]>
[ Upstream commit 31995571219c8ac30913d9c0dccad033fbb0b3da ]
Removing the legacy ioctl fallback made both hwtstamp NDOs mandatory. A device that only timestamps in its PHY implements neither, so SIOCSHWTSTAMP fails with EOPNOTSUPP before anything looks at the PHY and PTP stops working there.
The check only ever picked the legacy path. That path is gone, so drop it and test where the NDOs are actually called.
SIOCGHWTSTAMP is new here, not restored. The old path went through phy_mii_ioctl(), which only handled SIOCSHWTSTAMP.
Such a device now returns -ENODEV while absent instead of -EOPNOTSUPP, like the ones that do implement the NDOs.
--- net/core/dev_ioctl.c | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-)
net/core/dev_ioctl.c +9 -16
@@ -276,19 +276,18 @@ int dev_get_hwtstamp_phylib(struct net_device *dev,if (phy_is_default_hwtstamp (dev->phydev))return phy_hwtstamp_get (dev->phydev, cfg); +if (!dev->netdev_ops->ndo_hwtstamp_get) +return -EOPNOTSUPP ; +return dev->netdev_ops->ndo_hwtstamp_get (dev, cfg); }static int dev_get_hwtstamp (struct net_device *dev,struct ifreq *ifr) { -const struct net_device_ops *ops = dev->netdev_ops;struct kernel_hwtstamp_config kernel_cfg = {};struct hwtstamp_config cfg;int err; -if (!ops->ndo_hwtstamp_get) -return -EOPNOTSUPP ; -if (!netif_device_present (dev))return -ENODEV ;
@@ -359,12 +358,18 @@ int dev_set_hwtstamp_phylib(struct net_device *dev, cfg->source = phy_ts ?HWTSTAMP_SOURCE_PHYLIB :HWTSTAMP_SOURCE_NETDEV ;if (phy_ts && dev->see_all_hwtstamp_requests) { +if (!ops->ndo_hwtstamp_get) +return -EOPNOTSUPP ; + err = ops->ndo_hwtstamp_get (dev, &old_cfg);if (err)return err; }if (!phy_ts || dev->see_all_hwtstamp_requests) { +if (!ops->ndo_hwtstamp_set) +return -EOPNOTSUPP ; + err = ops->ndo_hwtstamp_set (dev, cfg, extack);if (err) {if (extack->_msg)
@@ -390,7 +395,6 @@ int dev_set_hwtstamp_phylib(struct net_device *dev,static int dev_set_hwtstamp (struct net_device *dev,struct ifreq *ifr) { -const struct net_device_ops *ops = dev->netdev_ops;struct kernel_hwtstamp_config kernel_cfg = {};struct netlink_ext_ack extack = {};struct hwtstamp_config cfg;
@@ -413,9 +417,6 @@ static int dev_set_hwtstamp(struct net_device *dev, struct ifreq *ifr)return err; } -if (!ops->ndo_hwtstamp_set) -return -EOPNOTSUPP ; -if (!netif_device_present (dev))return -ENODEV ;
@@ -441,15 +442,11 @@ static int dev_set_hwtstamp(struct net_device *dev, struct ifreq *ifr)int generic_hwtstamp_get_lower (struct net_device *dev,struct kernel_hwtstamp_config *kernel_cfg) { -const struct net_device_ops *ops = dev->netdev_ops;int err;if (!netif_device_present (dev))return -ENODEV ; -if (!ops->ndo_hwtstamp_get) -return -EOPNOTSUPP ; -netdev_lock_ops (dev); err =dev_get_hwtstamp_phylib (dev, kernel_cfg);netdev_unlock_ops (dev);
@@ -462,15 +459,11 @@ int generic_hwtstamp_set_lower(struct net_device *dev,struct kernel_hwtstamp_config *kernel_cfg, struct netlink_ext_ack *extack) { - const struct net_device_ops *ops = dev->netdev_ops; int err;if (!netif_device_present (dev))return -ENODEV ; -if (!ops->ndo_hwtstamp_set) -return -EOPNOTSUPP ; -netdev_lock_ops (dev); err =dev_set_hwtstamp_phylib (dev, kernel_cfg, extack);netdev_unlock_ops (dev);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 134/457 dpll: use exact lookup for reference sync pin id
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ivan Vecera <[email protected]>
[ Upstream commit 7cce782d8327b7291334c4a304cf3fd909a74d9d ]
dpll_pin_ref_sync_state_set() looks up the reference sync pin in the pin->ref_sync_pins xarray, which is keyed by the sync pin's id (see dpll_pin_ref_sync_pair_add() using xa_insert() with ref_sync_pin->id). The pin id to operate on is supplied by userspace via DPLL_A_PIN_ID.
The lookup however used xa_find() with a ULONG_MAX limit, which returns the first present entry with an index greater than or equal to the requested id, not the entry stored exactly at that id. If userspace passes an id that is not paired as a reference sync pin, but another pin with a higher id is present in the xarray, xa_find() silently returns that wrong pin and the subsequent ref_sync_set() operates on it. The request only fails when the given id is larger than every present key.
Use xa_load() for an exact-key lookup instead, mirroring the deletion path in dpll_pin_ref_sync_pair_del().
--- drivers/dpll/dpll_netlink.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
drivers/dpll/dpll_netlink.c +1 -2
@@ -1282,8 +1282,7 @@ dpll_pin_ref_sync_state_set(struct dpll_pin *pin,unsigned long i;int ret; - ref_sync_pin =xa_find (&pin->ref_sync_pins, &ref_sync_pin_idx, -ULONG_MAX ,XA_PRESENT ); + ref_sync_pin =xa_load (&pin->ref_sync_pins, ref_sync_pin_idx);if (!ref_sync_pin) {NL_SET_ERR_MSG (extack,"reference sync pin not found" );return -EINVAL ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 135/457 net: usb: sr9700: include receive overhead in the length check
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pengpeng Hou <[email protected]>
[ Upstream commit c06bde80ae7a7b595732f7cabcb92cf08db9d56a ]
The receive fixup subtracts the Ethernet CRC from the reported packet length, but compares that payload length against the whole remaining receive buffer. The following copy starts after the three-byte header, and the cursor advance consumes both that header and the four-byte CRC.
Require the payload to fit after SR_RX_OVERHEAD before copying it or advancing to the next packet. The loop already ensures that the remaining buffer is larger than the overhead, so the subtraction is safe.
The issue was found by our static-analysis tool.
--- drivers/net/usb/sr9700.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/net/usb/sr9700.c +2 -1
@@ -355,7 +355,8 @@ static int sr9700_rx_fixup(struct usbnet *dev, struct sk_buff *skb)/* ignore the CRC length */ len = (skb->data[1 ] | (skb->data[2 ] <<8 )) -4 ; -if (len >ETH_FRAME_LEN || len > skb->len || len <0 ) +if (len >ETH_FRAME_LEN || len <0 || + len > skb->len -SR_RX_OVERHEAD )return 0 ;/* the last packet of current skb */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 136/457 ipv6: Fix dst leak for uncached routes.
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kuniyuki Iwashima <[email protected]>
[ Upstream commit be31fe6333f534155e6b408f1ef6d77974bb41aa ]
ip6_route_output_flags(), ip6_rt_put_flags(), and ip6_dst_check() detect an uncached route by list_empty(&rt->dst.rt_uncached), which replaced the static DST_NOCACHE flag check in commit a4c2fd7f7891 ("net: remove DST_NOCACHE flag").
When a device is unregistered, rt6_uncached_list_flush_dev() unlinks uncached routes tied to the device from rt6_uncached_list.
Previously, they were moved to another list with list_move() (__list_del_entry() + list_add()), and since commit 98aa546af5e4 ("inet: remove (struct uncached_list)->quarantine"), the routes are just unlinked with list_del_init().
If list_del_init() runs concurrently, list_empty() evaluates to true; ip6_route_output_flags() calls dst_hold_safe() incorrectly and ip6_rt_put_flags() skips ip6_rt_put(), leaking dst, and thus dev tied via rt->from as well.
The same race is partially fixed by commit 9a6f0c4d5796 ("dst:
fix races in rt6_uncached_list_del() and rt_del_uncached_list()").
Let's check rt6->dst.rt_uncached_list instead.
Note that IPv4 does not have the same issue.
--- include/net/ip6_route.h | 4 ++-- net/ipv6/route.c | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-)
include/net/ip6_route.h +2 -2
@@ -101,12 +101,12 @@ static inline struct dst_entry *ip6_route_output(struct net *net, }/* Only conditionally release dst if flags indicates -* !RT6_LOOKUP_F_DST_NOREF or dst is in uncached_list. +* !RT6_LOOKUP_F_DST_NOREF or dst is uncached. */ static inline void ip6_rt_put_flags (struct rt6_info *rt,int flags) {if (!(flags &RT6_LOOKUP_F_DST_NOREF ) || - !list_empty (&rt->dst.rt_uncached)) + rt->dst.rt_uncached_list)ip6_rt_put (rt); }
net/ipv6/route.c +4 -3
@@ -139,6 +139,7 @@ void rt6_uncached_list_add(struct rt6_info *rt) {struct uncached_list *ul =raw_cpu_ptr (&rt6_uncached_list); +/* Set once and never cleared: non-NULL marks an uncached route. */ rt->dst.rt_uncached_list = ul;spin_lock_bh (&ul->lock);
@@ -2726,8 +2727,8 @@ struct dst_entry *ip6_route_output_flags(struct net *net,rcu_read_lock (); dst =ip6_route_output_flags_noref (net, sk, fl6, flags); rt6 =dst_rt6_info (dst); -/* For dst cached in uncached_list, refcnt is already taken. */ -if (list_empty (&rt6->dst.rt_uncached) && !dst_hold_safe (dst)) { +/* For an uncached dst, refcnt is already taken. */ +if (!rt6->dst.rt_uncached_list && !dst_hold_safe (dst)) { dst = &net->ipv6.ip6_null_entry->dst;dst_hold (dst); }
@@ -2836,7 +2837,7 @@ INDIRECT_CALLABLE_SCOPE struct dst_entry *ip6_dst_check(struct dst_entry *dst, from =rcu_dereference (rt->from);if (from && (rt->rt6i_flags &RTF_PCPU || -unlikely (!list_empty (&rt->dst.rt_uncached)))) +unlikely (rt->dst.rt_uncached_list))) dst_ret =rt6_dst_from_check (rt, from, cookie);else dst_ret =rt6_check (rt, from, cookie);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 137/457 udp: relocate a connected socket in the 4-tuple hash table on re-connect
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Shardul Bankar <[email protected]>
[ Upstream commit 5fd0783b99d4af98f65cd58b56ec203d1d426104 ]
A connected UDP socket that connects again to a different peer is not re-filed in the 4-tuple hash table:
sk binds to 127.0.0.1:21001
sk connects to 127.0.0.2:20001 // filed under hash(sk, peer1)
sk connects to 127.0.0.3:20002 // still filed under hash(sk, peer1)
packet from 127.0.0.3:20002 // hash(sk, peer2) misses, so the
// lookup falls back to scoring the
// hash2 chain for this address
// and portudp_lib_hash4() returns early when the socket is already hashed, assuming ->rehash() relocates it. ->rehash() runs from __ip{4,6}_datagram_connect() only while the receive address is unset, which a second connect never is:
the first connect assigns it, whether the socket was bound to a specific address or to the wildcard. commit 644f9108f3a5 ("udp: Make rehash4 independent in udp_lib_rehash()") added that early return and named connect(AF_UNSPEC) as the way around it. That workaround does not help a socket with both SOCK_BINDADDR_LOCK and SOCK_BINDPORT_LOCK set, because __udp_disconnect() skips ->rehash() for the first and ->unhash() for the second.
Delivery is correct either way.
Relocate the socket when the hash it is filed under differs from the one requested, which is what commit 78c91ae2c6de ("ipv4/udp: Add 4-tuple hash for connected socket") did before the early return became unconditional. It is done here under hslot->lock, which that version did not take, to match udp_lib_rehash() and udp_lib_unhash(). hslot2 is unchanged, so hash4_cnt needs no adjustment, as in udp_lib_rehash(). A first connect is unaffected, and IPv6 shares the code.
With 500 sockets on the port, a re-connected socket measured 522,553 pps without this change and 2,055,078 with it. The UDP side was noted as remaining work in [1].
--- net/ipv4/udp.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-)
net/ipv4/udp.c +14 -5
@@ -616,14 +616,23 @@ void udp_lib_hash4(struct sock *sk, u16 hash)struct net *net =sock_net (sk);struct udp_table *udptable; -/* Connected udp socket can re-connect to another remote address, which -* will be handled by rehash. Thus no need to redo hash4 here. + udptable = net->ipv4.udp_table; + hslot =udp_hashslot (udptable, net,udp_sk (sk)->udp_port_hash); + +/* A connected socket can re-connect to another address. rehash() +* relocates it, but only runs when the local address changes, so a +* socket bound to a specific address would stay filed under the +* previous peer's hash. Move it here. */ -if (udp_hashed4 (sk)) +if (udp_hashed4 (sk)) { +if (udp_sk (sk)->udp_lrpa_hash != hash) { +spin_lock_bh (&hslot->lock); +udp_rehash4 (udptable, sk, hash); +spin_unlock_bh (&hslot->lock); + }return ; + } - udptable = net->ipv4.udp_table; - hslot =udp_hashslot (udptable, net,udp_sk (sk)->udp_port_hash); hslot2 =udp_hashslot2 (udptable,udp_sk (sk)->udp_portaddr_hash); hslot4 =udp_hashslot4 (udptable, hash);udp_sk (sk)->udp_lrpa_hash = hash;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 138/457 udp: remove a disconnected socket from the 4-tuple hash table
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Shardul Bankar <[email protected]>
[ Upstream commit 9e95b1a94c9c49b4ba722251bbca2759b9c51737 ]
A UDP socket bound to a specific address and port keeps its entry in the 4-tuple hash table after it is disconnected:
sk binds to 127.0.0.1:21001
sk connects to 127.0.0.2:20001 // filed in the 4-tuple table
sk disconnects, connect(AF_UNSPEC) // still filed, peer now 0.0.0.0:0__udp_disconnect() takes a socket out of that table only as a side effect of ->rehash() or ->unhash(), and it skips ->rehash() when SOCK_BINDADDR_LOCK is set and ->unhash() when SOCK_BINDPORT_LOCK is set. commit 6996a2d2d0a6 ("udp: Unhash auto-bound connected sk from 4-tuple hash table when disconnected.") fixed the same end state for a wildcard-bound socket, by a path this one does not take.
The entry is counted whether or not anything hits it. hash4_cnt on the hash2 slot stays raised for as long as the socket lives, so udp_has_hash4() keeps sending every packet for that address and port through the 4-tuple lookup first.
On IPv6 it can also be hit. __udp_disconnect() does not clear sk_v6_daddr, so udp_v6_rehash() files the entry under the peer the socket was connected to with a zero dport, and inet6_match() compares that same
field: a datagram from the former peer with a zero source port matches, and source port zero is accepted on receive. On IPv4 the peer is cleared, so a match would need a zero source address as well, which the routing layer rejects as martian. The stale sk_v6_daddr is a separate defect, not addressed here; removing the entry closes this path either way.
The entry can also be relocated. __udp_disconnect() clears sk_bound_dev_if, so a subsequent SO_BINDTODEVICE calls ->rehash(), and because the receive address is still specific udp_lib_rehash() moves the entry instead of removing it, into the bucket that (rcv_saddr, num, 0, 0) hashes to -- a pure function of the address and port, so every socket reaching this state on one address and port collects in one bucket. The bucket cannot be chosen from outside, as udp_ehashfn() is seeded with a per-boot secret. This last one became reachable only with commit 644f9108f3a5 ("udp: Make rehash4 independent in udp_lib_rehash()"), which moved the hash4 handling out of a branch a disconnected socket does not take; the stale entry itself dates from the commit in Fixes.
Take the socket out of the table before __udp_disconnect() runs, while it still matches how it was filed. This also reaches the wildcard case ahead of udp_lib_rehash()'s udp_unhash4() branch, leaving that branch unreachable from udp_disconnect(); removing it belongs in net-next. udp_disconnect() and udp_abort() are the only UDP entries into __udp_disconnect(), which is shared with raw, ping and l2tp sockets that are not struct udp_sock:
ping_prot.obj_size is sizeof(struct inet_sock), so udp_hashed4() on one would read past the allocation.
--- net/ipv4/udp.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+)
net/ipv4/udp.c +23 -0
@@ -2193,9 +2193,31 @@ int __udp_disconnect(struct sock *sk, int flags) }EXPORT_SYMBOL (__udp_disconnect); +/* __udp_disconnect() takes a socket out of the 4-tuple hash table only via +* ->rehash() or ->unhash(), and neither runs for a socket bound to a +* specific address and port. Remove it here, before its peer is cleared. +*/ +static void udp_unhash4_on_disconnect (struct sock *sk) +{ +struct net *net =sock_net (sk); +struct udp_table *udptable; +struct udp_hslot *hslot; + +if (!udp_hashed4 (sk)) +return ; + + udptable = net->ipv4.udp_table; + hslot =udp_hashslot (udptable, net,udp_sk (sk)->udp_port_hash); + +spin_lock_bh (&hslot->lock); +udp_unhash4 (udptable, sk); +spin_unlock_bh (&hslot->lock); +} +int udp_disconnect (struct sock *sk,int flags) {lock_sock (sk); +udp_unhash4_on_disconnect (sk);__udp_disconnect (sk, flags);release_sock (sk);return 0 ;
@@ -3107,6 +3129,7 @@ int udp_abort(struct sock *sk, int err) sk->sk_err = err;sk_error_report (sk); +udp_unhash4_on_disconnect (sk);__udp_disconnect (sk,0 ); out:
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 139/457 tg3: clean up PHYLIB resources on probe failure
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Myeonghun Pak <[email protected]>
[ Upstream commit a92e1a412c53dc0d9ad639e7abf8b3fc70a5b6ad ]
tg3_get_invariants() can register an MDIO bus and connect a PHY for USE_PHYLIB devices. If tg3_init_one() later fails, its common error path releases the mappings and netdev without undoing those PHYLIB resources.
Disconnect the PHY and unregister the MDIO bus before the remaining teardown. Guard PHY cleanup with USE_PHYLIB to match tg3_phy_init(), and call tg3_mdio_fini() unconditionally to match tg3_mdio_init(). The existing IS_CONNECTED and MDIOBUS_INITED flags make both helpers safe when initialization only completed partially.
This issue was identified during our ongoing static-analysis research while reviewing kernel code.
--- drivers/net/ethernet/broadcom/tg3.c | 4 ++++ 1 file changed, 4 insertions(+)
drivers/net/ethernet/broadcom/tg3.c +4 -0
@@ -18047,6 +18047,10 @@ static int tg3_init_one(struct pci_dev *pdev,return 0 ; err_out_apeunmap: +if (tg3_flag (tp,USE_PHYLIB )) +tg3_phy_fini (tp); +tg3_mdio_fini (tp); +if (tp->aperegs) {iounmap (tp->aperegs); tp->aperegs =NULL ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 140/457 drm/i915/psr: Clear stale sel fetch enable bits on sel fetch disable
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Nemesa Garg <[email protected]>
[ Upstream commit 2777ec9852277a06ae68fee0c4f1a32783e4a999 ]
Selective fetch is dropped while pipe CRC is active, and the planes keep their SEL_FETCH_PLANE_CTL / SEL_FETCH_CUR_CTL enable bit set in hardware over that. A plane disabled while selective fetch is off never gets the bit cleared, as the disable path is guarded by enable_psr2_sel_fetch. Once selective fetch comes back the hardware resumes fetching for a plane that is no longer enabled and keeps its DDB range reserved.
Clear the bits as selective fetch is turned off instead. Atomic check has both the old and the new crtc state, so record the transition there and let the plane and cursor arm paths write the registers to 0 for that commit.
v2: Drop the old_crtc_state->hw.active check. [Jouni]
--- drivers/gpu/drm/i915/display/intel_cursor.c | 7 +++++-- .../gpu/drm/i915/display/intel_display_types.h | 2 ++ drivers/gpu/drm/i915/display/intel_psr.c | 15 +++++++++++++++ .../gpu/drm/i915/display/skl_universal_plane.c | 9 ++++----- 4 files changed, 26 insertions(+), 7 deletions(-)
drivers/gpu/drm/i915/display/intel_cursor.c +5 -2
@@ -536,7 +536,8 @@ static void i9xx_cursor_disable_sel_fetch_arm(struct intel_dsb *dsb,struct intel_display *display =to_intel_display (plane);enum pipe pipe = plane->pipe; -if (!crtc_state->enable_psr2_sel_fetch) +if (!crtc_state->enable_psr2_sel_fetch && + !crtc_state->clear_psr2_sel_fetch)return ;intel_de_write_dsb (display, dsb,SEL_FETCH_CUR_CTL (pipe),0 );
@@ -569,8 +570,10 @@ static void i9xx_cursor_update_sel_fetch_arm(struct intel_dsb *dsb,struct intel_display *display =to_intel_display (plane);enum pipe pipe = plane->pipe; -if (!crtc_state->enable_psr2_sel_fetch) +if (!crtc_state->enable_psr2_sel_fetch) { +i9xx_cursor_disable_sel_fetch_arm (dsb, plane, crtc_state);return ; + }if (drm_rect_height (&plane_state->psr2_sel_fetch_area) >0 ) {if (crtc_state->enable_psr2_su_region_et) {
drivers/gpu/drm/i915/display/intel_display_types.h +2 -0
@@ -1177,6 +1177,8 @@ struct intel_crtc_state {bool has_sel_update;bool enable_psr2_sel_fetch;bool enable_psr2_su_region_et; +/* Drop the stale selective fetch enable bits as selective fetch is turned off */ +bool clear_psr2_sel_fetch;bool req_psr2_sdp_prior_scanline;bool has_panel_replay;bool link_off_after_as_sdp_when_pr_active;
drivers/gpu/drm/i915/display/intel_psr.c +15 -0
@@ -2933,6 +2933,8 @@ int intel_psr2_sel_fetch_update(struct intel_atomic_state *state, struct intel_crtc *crtc) {struct intel_display *display =to_intel_display (state); +const struct intel_crtc_state *old_crtc_state = +intel_atomic_get_old_crtc_state (state, crtc);struct intel_crtc_state *crtc_state =intel_atomic_get_new_crtc_state (state, crtc);struct intel_plane_state *new_plane_state, *old_plane_state;struct intel_plane *plane;
@@ -2945,6 +2947,19 @@ int intel_psr2_sel_fetch_update(struct intel_atomic_state *state,bool full_update = false, su_area_changed;int i, ret; +/* +* Selective fetch is not always usable, for instance it is dropped +* while pipe CRC is active. The planes keep their selective fetch +* enable bit set in hardware over that, and a plane disabled while +* selective fetch is off never gets the bit cleared. Once selective +* fetch comes back the hardware would resume fetching for a plane that +* is no longer enabled and keep its DDB range reserved, so have the +* plane update drop the bit for every plane of the pipe as selective +* fetch is turned off. +*/ + crtc_state->clear_psr2_sel_fetch = old_crtc_state->enable_psr2_sel_fetch && + !crtc_state->enable_psr2_sel_fetch; +if (!crtc_state->enable_psr2_sel_fetch)return 0 ;
drivers/gpu/drm/i915/display/skl_universal_plane.c +4 -5
@@ -885,7 +885,8 @@ static void icl_plane_disable_sel_fetch_arm(struct intel_dsb *dsb,struct intel_display *display =to_intel_display (plane);enum pipe pipe = plane->pipe; -if (!crtc_state->enable_psr2_sel_fetch) +if (!crtc_state->enable_psr2_sel_fetch && + !crtc_state->clear_psr2_sel_fetch)return ;intel_de_write_dsb (display, dsb,SEL_FETCH_PLANE_CTL (pipe, plane->id),0 );
@@ -1634,10 +1635,8 @@ static void icl_plane_update_sel_fetch_arm(struct intel_dsb *dsb,struct intel_display *display =to_intel_display (plane);enum pipe pipe = plane->pipe; -if (!crtc_state->enable_psr2_sel_fetch) -return ; - -if (drm_rect_height (&plane_state->psr2_sel_fetch_area) >0 ) +if (crtc_state->enable_psr2_sel_fetch && +drm_rect_height (&plane_state->psr2_sel_fetch_area) >0 )intel_de_write_dsb (display, dsb,SEL_FETCH_PLANE_CTL (pipe, plane->id),SEL_FETCH_PLANE_CTL_ENABLE );else
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 141/457 s390/debug: Do not register views for failed static debug areas
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mikhail Zaslonko <[email protected]>
[ Upstream commit 28e29992b034acffc9342df216c06097825ce610 ]
__REGISTER_STATIC_DEBUG_INFO() calls debug_register_view() unconditionally, even when debug_register_static() has failed. In that case _debug_register() was never reached and id->debugfs_root_entry is still NULL, so debugfs_create_file() places the view file in the debugfs root directory. For sclp_err this leaves a /sys/kernel/debug/hex_ascii file with nothing to indicate which debug log it belongs to.
debug_register_static() is not exported and the macro is its only caller, so let it return an error code and skip the view registration when it fails. No debugfs files are created for such an area then.
Reproduce by booting with s390dbf=sclp_err::100000000. The sclp_err registration fails, no s390dbf/sclp_err/ directory is created, and a hex_ascii file appears in the debugfs root instead.
--- arch/s390/include/asm/debug.h | 8 ++++++-- arch/s390/kernel/debug.c | 12 +++++++++--- 2 files changed, 15 insertions(+), 5 deletions(-)
arch/s390/include/asm/debug.h +6 -2
@@ -460,7 +460,11 @@ static int VNAME(var, active_entries)[EARLY_AREAS] __initdata#define __REGISTER_STATIC_DEBUG_INFO (var, name, pages, areas, view) \ static int __init VNAME(var, reg)(void) \ { \ - debug_register_static(&var, (pages), (areas)); \ + int rc; \ + \ + rc = debug_register_static(&var, (pages), (areas)); \ + if (rc) \ + return rc; \ debug_register_view(&var, (view)); \ return 0; \ } \
@@ -493,7 +497,7 @@ static debug_info_t __refdata var = \ static debug_info_t __used __section(".s390dbf_info") *VNAME(var, info) = &var; \ __REGISTER_STATIC_DEBUG_INFO(var, name, pages, nr_areas, view) -void debug_register_static (debug_info_t *id,int pages_per_area,int nr_areas); +int debug_register_static (debug_info_t *id,int pages_per_area,int nr_areas);#endif /* MODULE */
arch/s390/kernel/debug.c +9 -3
@@ -948,8 +948,12 @@ EXPORT_SYMBOL(debug_register);* * Note: This function is called automatically via an initcall generated by * DEFINE_STATIC_DEBUG_INFO. +* +* Return: +* - 0 on success +* - negative error code on failure */ -void debug_register_static (debug_info_t *id,int pages_per_area,int nr_areas) +int debug_register_static (debug_info_t *id,int pages_per_area,int nr_areas) {unsigned long flags;debug_info_t *copy;
@@ -957,7 +961,7 @@ void debug_register_static(debug_info_t *id, int pages_per_area, int nr_areas)if (!initialized) {pr_err ("Tried to register debug feature %s too early\n" , id->name); -return ; +return -EINVAL ; }debug_get_param (id->name, &id->level, &pages_per_area);
@@ -973,7 +977,7 @@ void debug_register_static(debug_info_t *id, int pages_per_area, int nr_areas) id->active_entries =NULL ;raw_spin_unlock_irqrestore (&id->lock, flags); -return ; +return -ENOMEM ; }/* Replace static trace area with dynamic copy. */
@@ -991,6 +995,8 @@ void debug_register_static(debug_info_t *id, int pages_per_area, int nr_areas)mutex_lock (&debug_mutex);_debug_register (id);mutex_unlock (&debug_mutex); + +return 0 ; }/* Remove debugfs entries. */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 142/457 s390/debug: Fix NULL pointer dereference in debug_info_copy()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mikhail Zaslonko <[email protected]>
[ Upstream commit 012bfcd5a51082d5a65f096dfb9ca652b5267965 ]
When debug_register_static() fails, it clears areas, active_pages and active_entries but leaves the area bounds unchanged. Copying such an area, either by opening its view file or via debug_dump(), makes debug_info_copy() dereference the NULL pointers.
Skip the copy loop when the source has no areas.
--- arch/s390/kernel/debug.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
arch/s390/kernel/debug.c +2 -1
@@ -432,7 +432,8 @@ static debug_info_t *debug_info_copy(debug_info_t *in, int mode)debug_info_free (rc); }while (1 ); -if (mode ==NO_AREAS ) +/* debug_register_static() failure leaves areas NULL, bounds intact */ +if (mode ==NO_AREAS || !in->areas) goto out;for (i =0 ; i < in->nr_areas; i++) {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 143/457 net: spacemit: clear TX descriptor on fragment mapping failure
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Muhammad Bilal <[email protected]>
[ Upstream commit 2d14720beb58870b15a52b236c3ab0be0e06e915 ]
emac_tx_mem_map() writes TX_DESC_0_OWN into the ring descriptor for every slot beyond old_head as soon as that slot's memset()'d local copy is committed with "*tx_desc_addr = tx_desc", i.e. before the buffers for that slot have necessarily all been mapped successfully. If emac_tx_map_frag() then fails on a later fragment, the err_free_skb path calls emac_free_tx_buf() to unmap and drop the skb, but leaves the already-written descriptor memory untouched, and tx_ring->head is never advanced past old_head (the "tx_ring->head = head" store is skipped by the goto).
So a slot between old_head and the rolled-back head can be left with TX_DESC_0_OWN set and buffer_addr_{1,2} pointing at DMA mappings that emac_free_tx_buf() just tore down, while software considers that slot free again. The next successful emac_tx_mem_map() call only rebuilds old_head itself; if the DMA engine auto-advances into the following descriptor once it finishes old_head's packet, it will fetch that stale, already-unmapped address.
emac_tx_clean_desc() already treats emac_free_tx_buf() and clearing the descriptor as a pair when reclaiming completed descriptors; do the same in the mapping failure path.
--- drivers/net/ethernet/spacemit/k1_emac.c | 3 +++ 1 file changed, 3 insertions(+)
drivers/net/ethernet/spacemit/k1_emac.c +3 -0
@@ -803,6 +803,9 @@ static void emac_tx_mem_map(struct emac_priv *priv, struct sk_buff *skb)while (i != head) {emac_free_tx_buf (priv, i); + tx_desc_addr = &((struct emac_desc *)tx_ring->desc_addr)[i]; +memset (tx_desc_addr,0 ,sizeof (*tx_desc_addr)); +if (++i == tx_ring->total_cnt) i =0 ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 144/457 net: ethernet: ti: netcp: fix pm_runtime usage counter leak on error
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: bui duc phuc <[email protected]>
[ Upstream commit ac4334522e4ba4a3b6710dd5d4cc98092824b8ca ]
pm_runtime_get_sync() leaves the runtime PM usage counter incremented even when it fails, but the error path in netcp_probe() does not call pm_runtime_put_noidle() to balance it, leaking a reference each time resume fails.
Use pm_runtime_resume_and_get() instead, which automatically drops the usage counter on failure, fixing the leak.
--- drivers/net/ethernet/ti/netcp_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/ethernet/ti/netcp_core.c +1 -1
@@ -2225,7 +2225,7 @@ static int netcp_probe(struct platform_device *pdev)return -ENOMEM ;pm_runtime_enable (&pdev->dev); - ret =pm_runtime_get_sync (&pdev->dev); + ret =pm_runtime_resume_and_get (&pdev->dev);if (ret <0 ) {dev_err (dev,"Failed to enable NETCP power-domain\n" );pm_runtime_disable (&pdev->dev);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 145/457 genetlink: report the real command id for dump-only ops in policy dumps
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jakub Kicinski <[email protected]>
[ Upstream commit 261e8a37ecbaf462cdf9c336d2b2f5056088401a ]
The op-to-policy map a CTRL_CMD_GETPOLICY dump returns is the only way for userspace to find out which policy index belongs to which command. ctrl_dumppolicy_put_op() tags the nest with doit->cmd, but an op which only has a dumpit has no doit and every path which fills the split ops in zeroes it out, so those entries all claim to be command 0. nlctrl's own CTRL_CMD_GETPOLICY and NETDEV_CMD_QSTATS_GET are both in that group:
[{'family-id': 16, 'op-policy': {'do': 0, 'dump': 0, 'op-id': 3}},
{'family-id': 16, 'op-policy': {'dump': 1, 'op-id': 0}},
ctrl_fill_info() gets this right - it uses the iterator's cmd for CTRL_ATTR_OP_ID - so the two introspection interfaces of the same family contradict each other today.
Pass the command in rather than reconstructing it from doit->cmd | dumpit->cmd inside the helper, both callers already have it.
--- net/netlink/genetlink.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-)
net/netlink/genetlink.c +5 -3
@@ -1656,7 +1656,7 @@ static void *ctrl_dumppolicy_prep(struct sk_buff *skb, }static int ctrl_dumppolicy_put_op (struct sk_buff *skb, -struct netlink_callback *cb, +struct netlink_callback *cb,u32 cmd,struct genl_split_ops *doit,struct genl_split_ops *dumpit) {
@@ -1677,7 +1677,7 @@ static int ctrl_dumppolicy_put_op(struct sk_buff *skb,if (!nest_pol) goto err; - nest_op =nla_nest_start (skb, doit->cmd); + nest_op =nla_nest_start (skb, cmd);if (!nest_op) goto err;
@@ -1721,7 +1721,8 @@ static int ctrl_dumppolicy(struct sk_buff *skb, struct netlink_callback *cb) &doit, &dumpit)))return -ENOENT ; -if (ctrl_dumppolicy_put_op (skb, cb, &doit, &dumpit)) +if (ctrl_dumppolicy_put_op (skb, cb, ctx->op, + &doit, &dumpit))return skb->len;/* done with the per-op policy index list */
@@ -1730,6 +1731,7 @@ static int ctrl_dumppolicy(struct sk_buff *skb, struct netlink_callback *cb)while (ctx->dump_map) {if (ctrl_dumppolicy_put_op (skb, cb, + ctx->op_iter->cmd, &ctx->op_iter->doit, &ctx->op_iter->dumpit))return skb->len;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 146/457 net: pcs: xpcs: fix clock reference leak on xpcs_init_clks failure
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Coia Prant <[email protected]>
[ Upstream commit 9892d71cf0ce3ff3d4fed2d9a3968fd4feb1c918 ]
xpcs_init_clks() takes references with clk_bulk_get_optional() and then enables them with clk_bulk_prepare_enable(). If the enable step fails, the function returns without dropping the references.
xpcs_create() handles the failure through out_free_data, which calls xpcs_free_data() but never xpcs_clear_clks(), so the clk references are leaked.
Add the missing clk_bulk_put() on the enable failure path. The prepare/enable side is already rolled back by clk_bulk_prepare_enable() itself.
--- drivers/net/pcs/pcs-xpcs.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/net/pcs/pcs-xpcs.c +3 -1
@@ -1545,8 +1545,10 @@ static int xpcs_init_clks(struct dw_xpcs *xpcs)return dev_err_probe (dev, ret,"Failed to get clocks\n" ); ret =clk_bulk_prepare_enable (DW_XPCS_NUM_CLKS , xpcs->clks); -if (ret) +if (ret) { +clk_bulk_put (DW_XPCS_NUM_CLKS , xpcs->clks);return dev_err_probe (dev, ret,"Failed to enable clocks\n" ); + }return 0 ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 147/457 PM: hibernate: Freeze kernel threads after image preallocation
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Florian Schmaus <[email protected]>
[ Upstream commit 41112a787f9182c7f2d27122817861e3f22ef928 ]
Commit 783c81098445 ("PM: hibernate: call preallocate_image() after freeze prepare") moved hibernate_preallocate_memory() after dpm_prepare() so that device drivers have the opportunity to release pinned/unswappable memory during their ->prepare() callback before memory is preallocated for the snapshot image.
However, that commit also placed hibernate_preallocate_memory() after freeze_kernel_threads(). While it was assumed during review that swap I/O submitted via submit_bio() is synchronous and would not depend on frozen kernel threads, this does not hold in practice. Calling hibernate_preallocate_memory() with kernel threads frozen leads to intermittent deadlocks during hibernation.
Inside hibernate_preallocate_memory(), shrink_all_memory() is invoked with .may_writepage = 1 and .may_swap = 1 to aggressively reclaim and swap out pages. Any writeback or swap I/O that relies on freezable kernel threads, block device helpers, or WQ_FREEZABLE workqueues (such as those in storage drivers, device mapper, or filesystems) deadlocks waiting on tasks that are stuck in the refrigerator.
Fix this by reordering hibernation_snapshot():
1. Call dpm_prepare(PMSG_FREEZE) first, allowing device drivers to release pinned resources while kernel threads are still active.
2. Call hibernate_preallocate_memory() second, performing page reclaim and swapout while storage layers, workqueues, and kernel threads are alive.
3. Call freeze_kernel_threads() third, only after all memory preallocation and swap I/O have completed.
Additionally, restore the call to swsusp_free() in the cleanup path so that preallocated image memory is properly freed if freeze_kernel_threads() fails or if TEST_FREEZER is enabled.
--- kernel/power/hibernate.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-)
kernel/power/hibernate.c +14 -12
@@ -408,9 +408,18 @@ int hibernation_snapshot(int platform_mode)if (error) goto Close; + error =dpm_prepare (PMSG_FREEZE ); +if (error) + goto Complete; + +/* Preallocate image memory before freezing kernel threads and shutting down devices. */ + error =hibernate_preallocate_memory (); +if (error) + goto Complete; + error =freeze_kernel_threads ();if (error) - goto Close; + goto Cleanup;if (hibernation_test (TEST_FREEZER )) {
@@ -422,15 +431,6 @@ int hibernation_snapshot(int platform_mode) goto Thaw; } - error =dpm_prepare (PMSG_FREEZE ); -if (error) - goto Complete; - -/* Preallocate image memory before shutting down devices. */ - error =hibernate_preallocate_memory (); -if (error) - goto Complete; -console_suspend_all ();pm_restrict_gfp_mask ();
@@ -464,10 +464,12 @@ int hibernation_snapshot(int platform_mode)platform_end (platform_mode);return error; - Complete: -dpm_complete (PMSG_RECOVER ); Thaw:thaw_kernel_threads (); + Cleanup: +swsusp_free (); + Complete: +dpm_complete (PMSG_RECOVER ); goto Close; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 148/457 thermal: gov_step_wise: Fix stale mitigation vote with non-zero lower bounds
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Manaf Meethalavalappu Pallikunhi <[email protected]>
[ Upstream commit ec0d89150a9381d591344a9f6f5428655c227a7f ]
When two or more thermal zones bind to a common cooling device and one zone uses a non-zero instance->lower value, there is a bug where the instance holds a stale mitigation vote even after its trip is cleared.
Problem scenario:
- thermal-zone1: Trip at 50°C, cooling-map with lower=0
- thermal-zone2: Trip at 55°C, cooling-map with lower=2
- Both zones share the same cooling device (e.g., CPU)
Issue flow:
1. Both trips trigger, zone1 requests state 5, zone2 also mitigates
2. Zone2 trip clears (temp < 53°C due to hysteresis)
3. When throttle=false and trend=THERMAL_TREND_DROPPING:
- Current code checks: if (cur_state <= instance->lower) return THERMAL_NO_TARGET
- Since cur_state (5) > instance->lower (2),
it returns instance->lower (2)
- This is the BUG where it returns instance->lower even though trip is cleared
4. Zone2's passive polling stops (tz->passive reaches 0) - no more updates for zone2
5. Zone2's stale vote of 2 persists indefinitely
6. Even when zone1 wants to reduce cooling to state, the cooling device cannot go below state 2 due to zone2's stale vote
When a trip is cleared (throttle == false), always return THERMAL_NO_TARGET instead of instance->lower. Remove the unnecessary check comparing cur_state with instance->lower. Since passive polling is already deactivated when the trip is cleared, the instance should always be deactivated regardless of its current cooling state. This ensures that instances with non-zero lower bounds do not retain stale mitigation votes after their trips are cleared.
--- drivers/thermal/gov_step_wise.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-)
drivers/thermal/gov_step_wise.c +4 -6
@@ -65,14 +65,12 @@ static unsigned long get_target_state(struct thermal_instance *instance,min (instance->lower +1 , instance->upper), instance->upper); }else if (trend ==THERMAL_TREND_DROPPING ) { -if (cur_state <= instance->lower) -return THERMAL_NO_TARGET ; -/* -* If 'throttle' is false, no mitigation is necessary, so -* request the lower state for this instance. +* If 'throttle' is false, no mitigation is necessary and +* passive polling is already deactivated, so clear this +* instance state by returning THERMAL_NO_TARGET. */ -return instance->lower; +return THERMAL_NO_TARGET ; }return instance->target;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 149/457 bpf: Fix bounds check for skb-backed dynptrs
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Emil Tsalapatis <[email protected]>
[ Upstream commit ed6eec97b534979dcf28b40c389cee57bd6561d4 ]
The skb_pointer_if_linear() function checks whether a memory region of length len starting at offset off into the skb is in the linear area, and returns a pointer to the region if so. The check currently subtracts between skb_headlen and offset of the check, and since skb_headlen is unsigned the subtraction can underflow. This causes the bounds check to spuriously pass and generate an arbitrary pointer of the form *(skb->data + off).
The only user of this helper is currently skb-backed BPF dynptr code. Returning the wrong pointer leads to the dynptr erroneously being backed with invalid memory.
Ensure the subtraction cannot underflow, and fail the check if it would. Use u64 arithmetic to also prevent overflow when calculating (skb_headlen(skb) - off) since off is unsigned.
--- include/linux/skbuff.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
include/linux/skbuff.h +4 -1
@@ -4372,7 +4372,10 @@ skb_header_pointer_careful(const struct sk_buff *skb, int offset,static inline void *__must_check skb_pointer_if_linear (const struct sk_buff *skb,int offset,int len) { -if (likely (skb_headlen (skb) - offset >= len)) +unsigned int uoffset = (unsigned int )offset; + +if (likely (uoffset <=skb_headlen (skb) && + (unsigned int )len <=skb_headlen (skb) - uoffset))return skb->data + offset;return NULL ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 150/457 bpf: Fix bpf_sock context code generation
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Emil Tsalapatis <[email protected]>
[ Upstream commit 4a4852376e3a2727ea40e61143d6d7c22bb6dfad ]
Currently, the ctx access code reads the rx_queue_mapping field with either a 4-byte or 2-byte load. The rest of the bits in the register are marked known zero by the verifier. However, the emitted ctx access code places in the register on certain the special value (-1) using BPF_MOV_IMM64, which gets sign-extended to turn on all the bits in the register. By shifting this value right, the program ends up with a value at runtime above what the verifier assumes is possible.
Fix this by ensuring the read value is as wide as the assumed size. Use MOV32 instructions instead of MOV64 instructions to keep the upper bits zero as assumed by the verifier. Also properly report the size of the destination variable (the bpf_sock field, 4 bytes) instead of the source (the socket field, 2 bytes).
--- net/core/filter.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-)
net/core/filter.c +4 -3
@@ -10403,11 +10403,12 @@ u32 bpf_sock_convert_ctx_access(enum bpf_access_type type, target_size)); *insn++ =BPF_JMP_IMM (BPF_JNE , si->dst_reg,NO_QUEUE_MAPPING ,1 ); - *insn++ =BPF_MOV64_IMM (si->dst_reg,-1 ); + *insn++ =BPF_MOV32_IMM (si->dst_reg,-1 );#else - *insn++ =BPF_MOV64_IMM (si->dst_reg,-1 ); - *target_size =2 ; + *insn++ =BPF_MOV32_IMM (si->dst_reg,-1 );#endif + *target_size = sizeof_field(struct bpf_sock, rx_queue_mapping); +break ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 151/457 bpf: Reject pkt arguments in mutating subprogs
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Emil Tsalapatis <[email protected]>
[ Upstream commit a6c1edfbe240e4377038a0e1d233ad81fde9a21b ]
The verifier tracks changes in how PTR_TO_PACKET registers' bounds are modified across subprog boundaries. PTR_TO_PACKET registers are actually passed as PTR_TO_MEM, which is assumed valid for the entire call. This is not the case with packet memory, where a pskb_* call may invalidate its memory region.
Reject BPF code that passes PTR_TO_PACKET pointers to subprogs that may mutate a packet. We cannot pass the pointer as a true PTR_TO_PACKET because we would also need to somehow pass the PTR_TO_PACKET_META or PTR_TO_PACKET_END to the subprog. Since we cannot avoid representing the pointer in the subprog as PTR_TO_MEM, only permit it if the subprog is guaranteed not to mutate the packet.
--- kernel/bpf/verifier.c | 10 ++++++++++ 1 file changed, 10 insertions(+)
kernel/bpf/verifier.c +10 -0
@@ -9264,6 +9264,16 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env, int subprog,return ret;if (check_mem_reg (env, reg, argno, arg->mem_size))return -EINVAL ; +/* +* PTR_TO_PACKET get passed as PTR_TO_MEM, preventing +* us from adjusting bounds tracking info. +*/ +if ((reg_is_pkt_pointer_any (reg) ||reg_is_dynptr_slice_pkt (reg)) && + sub->changes_pkt_data) { +bpf_log (log,"%s is a packet pointer, but func#%d may change packet data\n" , +reg_arg_name (env, argno), subprog); +return -EINVAL ; + }if (!(arg->arg_type &PTR_MAYBE_NULL ) && (type_may_be_null (reg->type) ||bpf_register_is_null (reg))) { bpf_log(log, "%s is expected to be non-NULL\n",
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 152/457 bpf, sockmap: Reject max_entries > INT_MAX in sock_map_alloc
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zhao Gongyi <[email protected]>
[ Upstream commit 814a81c842bd88f6bd8a4ce550d560df071a5d03 ]
sock_map_alloc() only rejects max_entries == 0 and otherwise allows any u32 value. sock_map_free() then walks the sks[] array with a signed int iterator:
int i; for (i = 0; i < stab->map.max_entries; i++) struct sock **psk = &stab->sks[i];
When a SOCKMAP is created with max_entries = 0xffffffff (UINT_MAX), the allocation of 32 GiB can succeed on large-memory hosts. During free the counter reaches 0x80000000, wraps to INT_MIN, is sign-extended by movslq and turned into a ~16 GiB negative offset from stab->sks, pointing far below the allocation.
The faulting access is an xchg() write in sock_map_free(). Without KASAN, the same out-of-bounds write can fault on an unmapped vmalloc page or corrupt an unrelated allocation if that vmalloc address is populated. On a KASAN kernel with CONFIG_KASAN_VMALLOC=y, the shadow check for that address hits an unmapped shadow page and oopses first:
BUG: unable to handle page fault for address: fffff521b59c5a00 RIP: 0010:kasan_check_range+0x107/0x190 Call Trace: sock_map_free+0x93/0x190 map_create+0x68d/0xb30 __sys_bpf+0x21e/0x2e70
Vmcore confirmed stab->map.max_entries == 0xffffffff, stab->sks == 0xffffc911ace2d000, and the faulting address sks + (s64)INT_MIN * 8 exactly at 0xffffc90dace2d000. The same buggy path is reached on the normal close()/bpf_map_free_deferred() path whenever such a map is destroyed.
sock_map_alloc() used to bound its allocation size through bpf_map_charge_init(), but the bound was dropped when rlimit-based memory accounting was removed. Reject max_entries > INT_MAX at creation time so the signed iterator in sock_map_free() never sees a value that would overflow.
Triggered by syzkaller and reproduced on both a 6.6-based KASAN kernel and the upstream v7.3-rc2 kernel.
--- net/core/sock_map.c | 1 + 1 file changed, 1 insertion(+)
net/core/sock_map.c +1 -0
@@ -41,6 +41,7 @@ static struct bpf_map *sock_map_alloc(union bpf_attr *attr)struct bpf_stab *stab; if (attr->max_entries == 0 || + attr->max_entries > INT_MAX || attr->key_size != 4 || (attr->value_size != sizeof(u32) && attr->value_size != sizeof(u64)) ||
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 153/457 firewire: cdev: fix back-transition for iso_resource_auto client resource
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Takashi Sakamoto <[email protected]>
[ Upstream commit c6b51091cafff9ce6c03c1416aa13864d17ab97c ]
The todo member of iso_resource_auto structure represents the state of the client resource and normally transitions in the following order:
ISO_RES_AUTO_ALLOC -> ISO_RES_AUTO_REALLOC -> ISO_RES_AUTO_DEALLOC
However, concurrent access from the work item and the file descriptor release function can cause the state to transition backwards from ISO_RES_AUTO_DEALLOC to ISO_RES_AUTO_REALLOC.
Prevent the back-transition by checking the current state before updating it in the work item.
--- drivers/firewire/core-cdev.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
drivers/firewire/core-cdev.c +4 -2
@@ -1397,8 +1397,10 @@ static void iso_resource_auto_work(struct work_struct *work) }else {// Transit from allocation to reallocation, except if the client requested // deallocation in the meantime. -scoped_guard (spinlock_irq, &client->lock) - r->todo = ISO_RES_AUTO_REALLOC; + scoped_guard(spinlock_irq, &client->lock) { +if (r->todo ==ISO_RES_AUTO_ALLOC ) + r->todo =ISO_RES_AUTO_REALLOC ; + }if (channel >=0 ) r->params.channels_mask =BIT_ULL (channel);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 154/457 net/mlx5: Bridge, dont fail switchdev events of sibling eswitch ports
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bernardo Soares <[email protected]>
[ Upstream commit 35e6f970f553954d92ba20afa885139a8e7dd0d7 ]
mlx5 registers the bridge offload switchdev notifiers once per eswitch instance, but the notifier chains are global, so every instance sees every event and must filter out the ones that aren't its own. The existing filter, mlx5_esw_bridge_dev_same_hw(), only checks that the event netdevice sits on the same HCA - intentional for merged eswitch, where one bridge can span representors of several eswitches on one HCA - but same-HCA doesn't mean the instance actually has that port:
peer ports are only created reactively from NETDEV_CHANGEUPPER, so an instance brought up after a sibling PF's port was already enslaved has none. The port object and attribute handlers claim the event anyway once same-HW passes, then fail the port lookup and return -EINVAL, which gets reported to user space even though the owning instance already handled it (e.g. "bridge vlan add ... RTNETLINK answers:
Invalid argument"). Fix by filtering on the tracked port instead.
The same gap exists in the generic recursive lower-device walk used by attribute changes on a bridge with more than one representor enslaved
directly: mlx5_esw_bridge_lower_rep_vport_num_vhca_id_get() is entered with the bridge master netdevice, falls through to its generic netdev_for_each_lower_dev() loop, and returns as soon as the recursion into any one lower device yields a non-NULL rep - the underlying base case, mlx5_esw_bridge_rep_vport_num_vhca_id_get(), only checks mlx5_esw_bridge_dev_same_hw(), not ownership by the calling instance's br_offloads. mlx5_esw_bridge_lag_rep_get(), used for the LAG-master case, already filters on mlx5_esw_bridge_dev_same_esw() per candidate and so cannot select a sibling's rep; it is not the source of this bug. On a merged-eswitch HCA with a bridge spanning representors of more than one eswitch instance directly, the walk can return a sibling's rep instead of continuing to the one the calling instance actually owns, so the attribute change fails the same way as above. Fix by checking mlx5_esw_bridge_port_exists() at the point each rep is picked, same as the previous fix did for the notifier filter.
--- .../mellanox/mlx5/core/en/rep/bridge.c | 45 +++++++++++++++---- .../ethernet/mellanox/mlx5/core/esw/bridge.c | 6 +++ .../ethernet/mellanox/mlx5/core/esw/bridge.h | 2 + 3 files changed, 44 insertions(+), 9 deletions(-)
drivers/net/ethernet/mellanox/mlx5/core/en/rep/bridge.c +36 -9
@@ -85,9 +85,16 @@ mlx5_esw_bridge_lower_rep_vport_num_vhca_id_get(struct net_device *dev, struct mstruct net_device *lower_dev;struct list_head *iter; -if (netif_is_lag_master (dev) ||mlx5e_eswitch_rep (dev)) -return mlx5_esw_bridge_rep_vport_num_vhca_id_get (dev, esw, vport_num, - esw_owner_vhca_id); +if (netif_is_lag_master (dev) ||mlx5e_eswitch_rep (dev)) { +struct net_device *rep; + + rep =mlx5_esw_bridge_rep_vport_num_vhca_id_get (dev, esw, vport_num, + esw_owner_vhca_id); +if (rep && !mlx5_esw_bridge_port_exists (*vport_num, *esw_owner_vhca_id, + esw->br_offloads)) +return NULL ; +return rep; + }netdev_for_each_lower_dev (dev, lower_dev, iter) {struct net_device *rep;
@@ -104,6 +111,28 @@ mlx5_esw_bridge_lower_rep_vport_num_vhca_id_get(struct net_device *dev, struct mreturn NULL ; } +static bool mlx5_esw_bridge_rep_port_lookup (struct net_device *dev, +struct mlx5_esw_bridge_offloads *br_offloads, +u16 *vport_num,u16 *esw_owner_vhca_id) +{ +if (!mlx5_esw_bridge_rep_vport_num_vhca_id_get (dev, br_offloads->esw, vport_num, + esw_owner_vhca_id)) +return false; + +return mlx5_esw_bridge_port_exists (*vport_num, *esw_owner_vhca_id, br_offloads); +} + +static bool mlx5_esw_bridge_lower_rep_port_lookup (struct net_device *dev, +struct mlx5_esw_bridge_offloads *br_offloads, +u16 *vport_num,u16 *esw_owner_vhca_id) +{ +if (!mlx5_esw_bridge_lower_rep_vport_num_vhca_id_get (dev, br_offloads->esw, vport_num, + esw_owner_vhca_id)) +return false; + +return mlx5_esw_bridge_port_exists (*vport_num, *esw_owner_vhca_id, br_offloads); +} +static bool mlx5_esw_bridge_is_local (struct net_device *dev,struct net_device *rep,struct mlx5_eswitch *esw) {
@@ -218,8 +247,7 @@ mlx5_esw_bridge_port_obj_add(struct net_device *dev,u16 vport_num, esw_owner_vhca_id;int err; -if (!mlx5_esw_bridge_rep_vport_num_vhca_id_get (dev, br_offloads->esw, &vport_num, - &esw_owner_vhca_id)) +if (!mlx5_esw_bridge_rep_port_lookup (dev, br_offloads, &vport_num, &esw_owner_vhca_id))return 0 ; port_obj_info->handled = true;
@@ -251,8 +279,7 @@ mlx5_esw_bridge_port_obj_del(struct net_device *dev,const struct switchdev_obj_port_mdb *mdb;u16 vport_num, esw_owner_vhca_id; -if (!mlx5_esw_bridge_rep_vport_num_vhca_id_get (dev, br_offloads->esw, &vport_num, - &esw_owner_vhca_id)) +if (!mlx5_esw_bridge_rep_port_lookup (dev, br_offloads, &vport_num, &esw_owner_vhca_id))return 0 ; port_obj_info->handled = true;
@@ -283,8 +310,8 @@ mlx5_esw_bridge_port_obj_attr_set(struct net_device *dev,u16 vport_num, esw_owner_vhca_id;int err =0 ; -if (!mlx5_esw_bridge_lower_rep_vport_num_vhca_id_get (dev, br_offloads->esw, &vport_num, - &esw_owner_vhca_id)) +if (!mlx5_esw_bridge_lower_rep_port_lookup (dev, br_offloads, &vport_num, + &esw_owner_vhca_id))return 0 ; port_attr_info->handled = true;
drivers/net/ethernet/mellanox/mlx5/core/esw/bridge.c +6 -0
@@ -1686,6 +1686,12 @@ int mlx5_esw_bridge_vport_peer_unlink(struct net_device *br_netdev, u16 vport_nu extack); } +bool mlx5_esw_bridge_port_exists (u16 vport_num,u16 esw_owner_vhca_id, +struct mlx5_esw_bridge_offloads *br_offloads) +{ +return mlx5_esw_bridge_port_lookup (vport_num, esw_owner_vhca_id, br_offloads); +} +int mlx5_esw_bridge_port_vlan_add (u16 vport_num,u16 esw_owner_vhca_id,u16 vid,u16 flags,struct mlx5_esw_bridge_offloads *br_offloads,struct netlink_ext_ack *extack)
drivers/net/ethernet/mellanox/mlx5/core/esw/bridge.h +2 -0
@@ -80,6 +80,8 @@ int mlx5_esw_bridge_vlan_proto_set(u16 vport_num, u16 esw_owner_vhca_id, u16 pro struct mlx5_esw_bridge_offloads *br_offloads);int mlx5_esw_bridge_mcast_set (u16 vport_num,u16 esw_owner_vhca_id,bool enable,struct mlx5_esw_bridge_offloads *br_offloads); +bool mlx5_esw_bridge_port_exists (u16 vport_num,u16 esw_owner_vhca_id, +struct mlx5_esw_bridge_offloads *br_offloads);int mlx5_esw_bridge_port_vlan_add (u16 vport_num,u16 esw_owner_vhca_id,u16 vid,u16 flags,struct mlx5_esw_bridge_offloads *br_offloads,struct netlink_ext_ack *extack);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 155/457 net/mlx5: Bridge, dont fail unlink of untracked/unsupported peer ports
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bernardo Soares <[email protected]>
[ Upstream commit 2e51097c982b7b22382fda3202ba29f0ea33e8c0 ]
mlx5_esw_bridge_vport_unlink() returns -EINVAL when the port isn't tracked by this instance's br_offloads. This is reachable on a sibling instance that registered its notifier after the port was already
enslaved: it never saw the NETDEV_CHANGEUPPER link event, so peer_link() never created a peer port for it, but it does see the later unlink event and fails. Return 0 instead, and give mlx5_esw_bridge_vport_peer_unlink() the same merged_eswitch capability guard peer_link() already has, since without it peer_link() likewise never creates a port to unlink.
This also matters beyond the -EINVAL itself:mlx5_esw_bridge_switchdev_port_event() runs on the per-netns netdev_chain, and notifier_from_errno(-EINVAL) sets NOTIFY_STOP_MASK, which call_netdevice_notifiers_info() checks to stop calling further listeners on that chain - so the old -EINVAL silently dropped the event for any listener registered later on the same chain, even though none of it was visible to user space since __netdev_upper_dev_unlink() discards the return value.
--- drivers/net/ethernet/mellanox/mlx5/core/esw/bridge.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-)
drivers/net/ethernet/mellanox/mlx5/core/esw/bridge.c +5 -4
@@ -1649,10 +1649,8 @@ int mlx5_esw_bridge_vport_unlink(struct net_device *br_netdev, u16 vport_num,int err; port =mlx5_esw_bridge_port_lookup (vport_num, esw_owner_vhca_id, br_offloads); -if (!port) { -NL_SET_ERR_MSG_MOD (extack,"Port is not attached to any bridge" ); -return -EINVAL ; - } +if (!port) +return 0 ;if (port->bridge->ifindex != br_netdev->ifindex) {NL_SET_ERR_MSG_MOD (extack,"Port is attached to another bridge" );return -EINVAL ;
@@ -1682,6 +1680,9 @@ int mlx5_esw_bridge_vport_peer_unlink(struct net_device *br_netdev, u16 vport_nu struct mlx5_esw_bridge_offloads *br_offloads, struct netlink_ext_ack *extack) { +if (!MLX5_CAP_ESW (br_offloads->esw->dev, merged_eswitch)) +return 0 ; +return mlx5_esw_bridge_vport_unlink (br_netdev, vport_num, esw_owner_vhca_id, br_offloads, extack); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 156/457 sctp: hold asoc or transport before mod_timer() in timer handlers
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xin Long <[email protected]>
[ Upstream commit cae23ae3f7887a1cf8a75da38edcebeef695040f ]
Take the association or transport reference before rearming a timer in the timer handlers.
The existing code calls mod_timer() before taking the reference needed by the rearmed timer without holding the sock lock. This creates a race with timer cleanup: if the timer is deleted after mod_timer() returns but before the reference is taken, the cleanup path can drop the timer's reference and destroy the transport or association. The timer handler then takes a reference on the already freed object and eventually drops it, causing a refcount underflow.
Hold the object before mod_timer() and drop the reference if mod_timer() reports that the timer was already pending in timer handlers. Apply the same ordering to the proto-unreachable path, which can rearm a transport timer outside the timer handlers without holding the sock lock.
--- net/sctp/input.c | 7 ++++--- net/sctp/sm_sideeffect.c | 37 ++++++++++++++++++++++--------------- 2 files changed, 26 insertions(+), 18 deletions(-)
net/sctp/input.c +4 -3
@@ -436,9 +436,10 @@ void sctp_icmp_proto_unreachable(struct sock *sk,if (timer_pending (&t->proto_unreach_timer))return ;else { -if (!mod_timer (&t->proto_unreach_timer, - jiffies + (HZ /20 ))) -sctp_transport_hold (t); +sctp_transport_hold (t); +if (mod_timer (&t->proto_unreach_timer, + jiffies + (HZ /20 ))) +sctp_transport_put (t); } }else {struct net *net =sock_net (sk);
net/sctp/sm_sideeffect.c +22 -15
@@ -244,8 +244,9 @@ void sctp_generate_t3_rtx_event(struct timer_list *t)pr_debug ("%s: sock is busy\n" , __func__);/* Try again later. */ -if (!mod_timer (&transport->T3_rtx_timer, jiffies + (HZ /20 ))) -sctp_transport_hold (transport); +sctp_transport_hold (transport); +if (mod_timer (&transport->T3_rtx_timer, jiffies + (HZ /20 ))) +sctp_transport_put (transport); goto out_unlock; }
@@ -280,8 +281,9 @@ static void sctp_generate_timeout_event(struct sctp_association *asoc, timeout_type);/* Try again later. */ -if (!mod_timer (&asoc->timers[timeout_type], jiffies + (HZ /20 ))) -sctp_association_hold (asoc); +sctp_association_hold (asoc); +if (mod_timer (&asoc->timers[timeout_type], jiffies + (HZ /20 ))) +sctp_association_put (asoc); goto out_unlock; }
@@ -378,8 +380,9 @@ void sctp_generate_heartbeat_event(struct timer_list *t)pr_debug ("%s: sock is busy\n" , __func__);/* Try again later. */ -if (!mod_timer (&transport->hb_timer, jiffies + (HZ /20 ))) -sctp_transport_hold (transport); +sctp_transport_hold (transport); +if (mod_timer (&transport->hb_timer, jiffies + (HZ /20 ))) +sctp_transport_put (transport); goto out_unlock; }
@@ -388,8 +391,9 @@ void sctp_generate_heartbeat_event(struct timer_list *t) timeout =sctp_transport_timeout (transport);if (elapsed < timeout) { elapsed = timeout - elapsed; -if (!mod_timer (&transport->hb_timer, jiffies + elapsed)) -sctp_transport_hold (transport); +sctp_transport_hold (transport); +if (mod_timer (&transport->hb_timer, jiffies + elapsed)) +sctp_transport_put (transport); goto out_unlock; }
@@ -422,9 +426,10 @@ void sctp_generate_proto_unreach_event(struct timer_list *t)pr_debug ("%s: sock is busy\n" , __func__);/* Try again later. */ -if (!mod_timer (&transport->proto_unreach_timer, - jiffies + (HZ /20 ))) -sctp_transport_hold (transport); +sctp_transport_hold (transport); +if (mod_timer (&transport->proto_unreach_timer, + jiffies + (HZ /20 ))) +sctp_transport_put (transport); goto out_unlock; }
@@ -458,8 +463,9 @@ void sctp_generate_reconf_event(struct timer_list *t)pr_debug ("%s: sock is busy\n" , __func__);/* Try again later. */ -if (!mod_timer (&transport->reconf_timer, jiffies + (HZ /20 ))) -sctp_transport_hold (transport); +sctp_transport_hold (transport); +if (mod_timer (&transport->reconf_timer, jiffies + (HZ /20 ))) +sctp_transport_put (transport); goto out_unlock; }
@@ -495,8 +501,9 @@ void sctp_generate_probe_event(struct timer_list *t)pr_debug ("%s: sock is busy\n" , __func__);/* Try again later. */ -if (!mod_timer (&transport->probe_timer, jiffies + (HZ /20 ))) -sctp_transport_hold (transport); +sctp_transport_hold (transport); +if (mod_timer (&transport->probe_timer, jiffies + (HZ /20 ))) +sctp_transport_put (transport); goto out_unlock; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 157/457 net: stmmac: selftests: Support running selftests on DSA conduits
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Maxime Chevallier <[email protected]>
[ Upstream commit d68acbf93531abdb5b02b21994cd4c15a3c95b42 ]
Most stmmac selftests rely on dev_add_pack() to add custom handlers, that validate the packets sent to ourselves through MAC loopback.
However, when the stmmac-driven interface is a DSA CPU conduit, all frames that are received have ETH_P_XDSA as a protocol, even though they don't actually contain any tag as they come from the loopback and not the switch.
This will prevent any incoming packet to match our packet handlers.
Let's register a ETH_P_ALL packet handler when we detect that we're a DSA conduit, and use a proxy packet handler to filter the h_proto.
As this allows external frames to be received through our .func(), the packet handler is added after the dev->addr field is populated in our selftest attributes.
Note that we may still receive incoming packets from the switch, but these frames shouldn't interfere with the very specific frames used for selftests, and stmmac selftests in general aren't safe against external traffic interferences.
This was validated on a WPQ864 devkit for IPQ8064, that has the SoC connected to a QCA8k switch.
The ARP offload's packet handler is left alone, this feature is just not implemented in stmmac and due for removal.
--- .../stmicro/stmmac/stmmac_selftests.c | 89 +++++++++++++++---- 1 file changed, 71 insertions(+), 18 deletions(-)
drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c +71 -18
@@ -13,6 +13,7 @@#include <linux/ip.h> #include <linux/phy.h> #include <linux/udp.h> +#include <net/dsa.h> #include <net/pkt_cls.h> #include <net/pkt_sched.h> #include <net/tcp.h>
@@ -238,6 +239,9 @@ struct stmmac_test_priv {struct stmmac_packet_attrs *packet;struct packet_type pt;struct completion comp; +__be16 packet_type; +int (*func)(struct sk_buff *skb,struct net_device *ndev, +struct packet_type *pt,struct net_device *orig_ndev);int double_vlan;int vlan_id;int ok;
@@ -317,6 +321,50 @@ static int stmmac_test_loopback_validate(struct sk_buff *skb,return 0 ; } +static int stmmac_sft_filter (struct sk_buff *skb,struct net_device *ndev, +struct packet_type *pt, +struct net_device *orig_ndev) +{ +struct stmmac_test_priv *tpriv = pt->af_packet_priv; +struct ethhdr *hdr =eth_hdr (skb); +int ret =0 ; + +if (hdr->h_proto == tpriv->packet_type) { +struct sk_buff *nskb =skb_clone (skb,GFP_ATOMIC ); + +if (nskb) + ret = tpriv->func (nskb, ndev, pt, orig_ndev); + } + +kfree_skb (skb); +return ret; +} + +static void stmmac_sft_add_pack (struct packet_type *pt) +{ +struct stmmac_test_priv *tpriv = pt->af_packet_priv; + +if (netdev_uses_dsa (tpriv->pt.dev)) { + tpriv->packet_type = tpriv->pt.type; + tpriv->func = tpriv->pt.func; + +/* DSA conduit will report ETH_P_XDSA, so our packet handler +* won't match. Let's register a ETH_P_ALL match and filter +* manually in stmmac_sft_filter. +*/ + tpriv->pt.type =htons (ETH_P_ALL ); + tpriv->pt.func = stmmac_sft_filter; + tpriv->pt.ignore_outgoing = true; + } + +dev_add_pack (pt); +} + +static void stmmac_sft_remove_pack (struct packet_type *pt) +{ +dev_remove_pack (pt); +} +static int __stmmac_test_loopback (struct stmmac_priv *priv,struct stmmac_packet_attrs *attr) {
@@ -338,7 +386,7 @@ static int __stmmac_test_loopback(struct stmmac_priv *priv, tpriv->packet = attr;if (!attr->dont_wait) -dev_add_pack (&tpriv->pt); +stmmac_sft_add_pack (&tpriv->pt); skb =stmmac_test_get_udp_skb (priv, attr);if (!skb) {
@@ -361,7 +409,7 @@ static int __stmmac_test_loopback(struct stmmac_priv *priv, cleanup:if (!attr->dont_wait) -dev_remove_pack (&tpriv->pt); +stmmac_sft_remove_pack (&tpriv->pt);kfree (tpriv);return ret; }
@@ -787,7 +835,7 @@ static int stmmac_test_flowctrl(struct stmmac_priv *priv) tpriv->pt.func = stmmac_test_flowctrl_validate; tpriv->pt.dev = priv->dev; tpriv->pt.af_packet_priv = tpriv; -dev_add_pack (&tpriv->pt); +stmmac_sft_add_pack (&tpriv->pt);/* Compute minimum number of packets to make FIFO full */ pkt_count = rx_fifo_size;
@@ -843,7 +891,7 @@ static int stmmac_test_flowctrl(struct stmmac_priv *priv) cleanup:dev_mc_del (priv->dev, paddr);dev_set_promiscuity (priv->dev,-1 ); -dev_remove_pack (&tpriv->pt); +stmmac_sft_remove_pack (&tpriv->pt);kfree (tpriv);return ret; }
@@ -948,18 +996,20 @@ static int __stmmac_test_vlanfilt(struct stmmac_priv *priv)* HASH values. */ tpriv->vlan_id =0x123 ; -dev_add_pack (&tpriv->pt); ret =vlan_vid_add (priv->dev,htons (ETH_P_8021Q ), tpriv->vlan_id);if (ret) goto cleanup; + attr.vlan =1 ; + attr.dst = priv->dev->dev_addr; + attr.sport =9 ; + attr.dport =9 ; + +stmmac_sft_add_pack (&tpriv->pt); +for (i =0 ; i <4 ; i++) { - attr.vlan =1 ; attr.vlan_id_out = tpriv->vlan_id + i; - attr.dst = priv->dev->dev_addr; - attr.sport =9 ; - attr.dport =9 ; skb =stmmac_test_get_udp_skb (priv, &attr);if (!skb) {
@@ -986,9 +1036,9 @@ static int __stmmac_test_vlanfilt(struct stmmac_priv *priv) } vlan_del: +stmmac_sft_remove_pack (&tpriv->pt);vlan_vid_del (priv->dev,htons (ETH_P_8021Q ), tpriv->vlan_id); cleanup: -dev_remove_pack (&tpriv->pt);kfree (tpriv);return ret; }
@@ -1042,18 +1092,20 @@ static int __stmmac_test_dvlanfilt(struct stmmac_priv *priv)* HASH values. */ tpriv->vlan_id =0x123 ; -dev_add_pack (&tpriv->pt); ret =vlan_vid_add (priv->dev,htons (ETH_P_8021AD ), tpriv->vlan_id);if (ret) goto cleanup; + attr.vlan =2 ; + attr.dst = priv->dev->dev_addr; + attr.sport =9 ; + attr.dport =9 ; + +stmmac_sft_add_pack (&tpriv->pt); +for (i =0 ; i <4 ; i++) { - attr.vlan =2 ; attr.vlan_id_out = tpriv->vlan_id + i; - attr.dst = priv->dev->dev_addr; - attr.sport =9 ; - attr.dport =9 ; skb =stmmac_test_get_udp_skb (priv, &attr);if (!skb) {
@@ -1080,9 +1132,9 @@ static int __stmmac_test_dvlanfilt(struct stmmac_priv *priv) } vlan_del: +stmmac_sft_remove_pack (&tpriv->pt);vlan_vid_del (priv->dev,htons (ETH_P_8021AD ), tpriv->vlan_id); cleanup: -dev_remove_pack (&tpriv->pt);kfree (tpriv);return ret; }
@@ -1313,7 +1365,6 @@ static int stmmac_test_vlanoff_common(struct stmmac_priv *priv, bool svlan) tpriv->pt.af_packet_priv = tpriv; tpriv->packet = &attr; tpriv->vlan_id =0x123 ; -dev_add_pack (&tpriv->pt); ret =vlan_vid_add (priv->dev,htons (proto), tpriv->vlan_id);if (ret)
@@ -1321,6 +1372,8 @@ static int stmmac_test_vlanoff_common(struct stmmac_priv *priv, bool svlan) attr.dst = priv->dev->dev_addr; +stmmac_sft_add_pack (&tpriv->pt); + skb =stmmac_test_get_udp_skb (priv, &attr);if (!skb) { ret = -ENOMEM ;
@@ -1338,9 +1391,9 @@ static int stmmac_test_vlanoff_common(struct stmmac_priv *priv, bool svlan) ret = tpriv->ok ?0 : -ETIMEDOUT ; vlan_del: +stmmac_sft_remove_pack (&tpriv->pt);vlan_vid_del (priv->dev,htons (proto), tpriv->vlan_id); cleanup: -dev_remove_pack (&tpriv->pt);kfree (tpriv);return ret; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 158/457 net: stmmac: selftests: Validate EEE based on the actual LPI timer value
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Maxime Chevallier <[email protected]>
[ Upstream commit c8c1795aa8106293020a836d01e127e98f442925 ]
The EEE selftest is a 2-step test :
- It validates that we enter in LPI mode with the irq_tx_path_in_lpi_mode_n counter
- It then validates that we exit LPI when sending a frame, with the irq_tx_path_exit_lpi_mode_n counter.
The current state of the test lacks 2 main things :
- We don't know exactly when was the previous frame sent (it's from the previous selftest)
- The timeout is hardcoded, while the LPI is entered after a user-configurable delay. On top of that, the timeout loop uses a pre-decrement iterator (--retries) that actually only iterate nine times, so 900ms while the default LPI value is 1 second.
Let's therefore make it more deterministic :
- Send a frame at the beginning of the test
- Wait for more than the lpi timer value, we timeout after about twice the value,
- Then send another frame, and verify that we do go out of LPI, also with a timeout.
As LPI timer can get pretty high, bail out if LPI timer is over 5 seconds.
Note that the test's goal isn't to validate the LPI timer value itself, only that we enter/leave LPI mode.
--- .../stmicro/stmmac/stmmac_selftests.c | 44 ++++++++++++++++--- 1 file changed, 37 insertions(+), 7 deletions(-)
drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c +37 -7
@@ -31,6 +31,7 @@ struct stmmachdr { sizeof(struct stmmachdr))#define STMMAC_TEST_PKT_MAGIC 0xdeadcafecafedeadULL#define STMMAC_LB_TIMEOUT msecs_to_jiffies(200) +#define STMMAC_SFT_MAX_LPI (5 * USEC_PER_SEC)struct stmmac_packet_attrs {int vlan;
@@ -482,12 +483,16 @@ static int stmmac_test_mmc(struct stmmac_priv *priv)static int stmmac_test_eee (struct stmmac_priv *priv) {struct stmmac_extra_stats *initial, *final; -int retries =10 ; +unsigned long timeout, max_duration;int ret;if (!priv->dma_cap.eee || !priv->eee_active)return -EOPNOTSUPP ; +/* Bail out if the configured LPI timer is too long */ +if (priv->tx_lpi_timer >STMMAC_SFT_MAX_LPI ) +return -EOPNOTSUPP ; + initial =kzalloc_obj (*initial);if (!initial)return -ENOMEM ;
@@ -498,14 +503,21 @@ static int stmmac_test_eee(struct stmmac_priv *priv) goto out_free_initial; } +/* Snapshot stats, we want to count the in_lpi events. We may enter +* LPI just after the packet was sent. +*/ memcpy (initial, &priv->xstats,sizeof (*initial)); +/* Send a frame, then wait to enter LPI */ ret =stmmac_test_mac_loopback (priv);if (ret) goto out_free_final; + max_duration =usecs_to_jiffies (2 * priv->tx_lpi_timer); +/* We have no traffic in the line so, sooner or later it will go LPI */ -while (--retries) { + timeout = jiffies + max_duration; +while (!time_after (jiffies, timeout)) {memcpy (final, &priv->xstats,sizeof (*final)); if (final->irq_tx_path_in_lpi_mode_n >
@@ -514,20 +526,38 @@ static int stmmac_test_eee(struct stmmac_priv *priv)msleep (100 ); } -if (!retries) { +memcpy (final, &priv->xstats,sizeof (*final)); +if (final->irq_tx_path_in_lpi_mode_n <= + initial->irq_tx_path_in_lpi_mode_n) { ret = -ETIMEDOUT ; goto out_free_final; } -if (final->irq_tx_path_in_lpi_mode_n <= - initial->irq_tx_path_in_lpi_mode_n) { - ret = -EINVAL ; +/* Re-snapshot, as we want to measure exit_lpi events. We should be +* in LPI right now. +*/ +memcpy (initial, &priv->xstats,sizeof (*initial)); + +/* TX something so we go out of LPI */ + ret =stmmac_test_mac_loopback (priv); +if (ret) goto out_free_final; + +/* Wait for the exit LPI interrupt */ + timeout = jiffies + max_duration; +while (!time_after (jiffies, timeout)) { +memcpy (final, &priv->xstats,sizeof (*final)); + +if (final->irq_tx_path_exit_lpi_mode_n > + initial->irq_tx_path_exit_lpi_mode_n) +break ; +msleep (100 ); } +memcpy (final, &priv->xstats,sizeof (*final));if (final->irq_tx_path_exit_lpi_mode_n <= initial->irq_tx_path_exit_lpi_mode_n) { - ret = -EINVAL ; + ret = -ETIMEDOUT ; goto out_free_final; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 159/457 net: stmmac: selftests: Check the dev->features for S-TAG offload testing
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Maxime Chevallier <[email protected]>
[ Upstream commit ba804b23d76d278ee475b8427fa7c5623ce5e270 ]
The S-TAG offload insertion incorrectly checks the dvlan (double vlan) DMA cap, which is different than S-TAG support. Use NETIF_F_HW_VLAN_STAG_TX to check if the feature is supported instead.
Note that this flag isn't set in stmmac yet, but contrary to ARP offload, this is a feature that has a chance to get there eventually so let's leave the selftest here for now. It'll report -EOPNOTSUPP in the meantime.
--- drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c +1 -1
@@ -1435,7 +1435,7 @@ static int stmmac_test_vlanoff(struct stmmac_priv *priv)static int stmmac_test_svlanoff (struct stmmac_priv *priv) { -if (!priv->dma_cap.dvlan) +if (!(priv->dev->features &NETIF_F_HW_VLAN_STAG_TX ))return -EOPNOTSUPP ;return stmmac_test_vlanoff_common (priv, true); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 160/457 net: stmmac: selftests: Capture all packets for vlan checks
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Maxime Chevallier <[email protected]>
[ Upstream commit 960db6f65788c21249ea04a947d5c01e38d19294 ]
While we use vlan_vid_add to trigger the tag filtering machinery in the driver, there's no netdev associated to the VLAN. This causes the skb to arrive with empty skb->vlan_tci fields, as the packet is marked OTHERHOST in __netif_receive_skb_core(), and we fail our validation.
Let's use the proxy mechanism introduced for DSA, that registers a ETH_P_ALL packet handler that runs earlier, before the vlan netdev lookup, then filters for the correct ethertype before passing an skb clone to our validation function.
As we may receive external frames with the right tag from the outside, let's move the address check in the vlan validation function earlier.
--- .../stmicro/stmmac/stmmac_selftests.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-)
drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c +13 -6
@@ -243,6 +243,7 @@ struct stmmac_test_priv {__be16 packet_type;int (*func)(struct sk_buff *skb,struct net_device *ndev,struct packet_type *pt,struct net_device *orig_ndev); +bool capture_all;int double_vlan;int vlan_id;int ok;
@@ -345,13 +346,15 @@ static void stmmac_sft_add_pack(struct packet_type *pt) {struct stmmac_test_priv *tpriv = pt->af_packet_priv; -if (netdev_uses_dsa (tpriv->pt.dev)) { +if (netdev_uses_dsa (tpriv->pt.dev) || tpriv->capture_all) { tpriv->packet_type = tpriv->pt.type; tpriv->func = tpriv->pt.func;/* DSA conduit will report ETH_P_XDSA, so our packet handler * won't match. Let's register a ETH_P_ALL match and filter -* manually in stmmac_sft_filter. +* manually in stmmac_sft_filter. This is also useful for +* VLAN tests, to capture packets otherwise marked as +* OTHERHOST. */ tpriv->pt.type =htons (ETH_P_ALL ); tpriv->pt.func = stmmac_sft_filter;
@@ -963,6 +966,11 @@ static int stmmac_test_vlan_validate(struct sk_buff *skb, goto out;if (skb_headlen (skb) < (STMMAC_TEST_PKT_SIZE -ETH_HLEN )) goto out; + + ehdr = (struct ethhdr *)skb_mac_header (skb); +if (!ether_addr_equal_unaligned (ehdr->h_dest, tpriv->packet->dst)) + goto out; +if (tpriv->vlan_id) {if (skb->vlan_proto !=htons (proto)) goto out;
@@ -974,10 +982,6 @@ static int stmmac_test_vlan_validate(struct sk_buff *skb, } } - ehdr = (struct ethhdr *)skb_mac_header (skb); -if (!ether_addr_equal_unaligned (ehdr->h_dest, tpriv->packet->dst)) - goto out; - ihdr =ip_hdr (skb);if (tpriv->double_vlan) ihdr = (struct iphdr *)(skb_network_header (skb) +4 );
@@ -1019,6 +1023,7 @@ static int __stmmac_test_vlanfilt(struct stmmac_priv *priv) tpriv->pt.dev = priv->dev; tpriv->pt.af_packet_priv = tpriv; tpriv->packet = &attr; + tpriv->capture_all = true;/* * As we use HASH filtering, false positives may appear. This is a
@@ -1115,6 +1120,7 @@ static int __stmmac_test_dvlanfilt(struct stmmac_priv *priv) tpriv->pt.dev = priv->dev; tpriv->pt.af_packet_priv = tpriv; tpriv->packet = &attr; + tpriv->capture_all = true;/* * As we use HASH filtering, false positives may appear. This is a
@@ -1395,6 +1401,7 @@ static int stmmac_test_vlanoff_common(struct stmmac_priv *priv, bool svlan) tpriv->pt.af_packet_priv = tpriv; tpriv->packet = &attr; tpriv->vlan_id =0x123 ; + tpriv->capture_all = true; ret =vlan_vid_add (priv->dev,htons (proto), tpriv->vlan_id);if (ret)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 161/457 net: stmmac: dwmac4: Use the correct bufzise when the len is exactly 8K
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Maxime Chevallier <[email protected]>
[ Upstream commit b42e7012773a0e81e97a2dda6ef907f5147a6658 ]
DMA bufsize selection isn't made on the MTU but the actual frame length, so including the L2 header. On DWMAC4, if the len is exactly BUF_SIZE_8KiB, the next larger size is incorrectly selected.
Lets fix the comparison and while at it, rename the parameter from len to mtu.
--- drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.c | 4 ++-- drivers/net/ethernet/stmicro/stmmac/hwif.h | 2 +- drivers/net/ethernet/stmicro/stmmac/ring_mode.c | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-)
drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.c +2 -2
@@ -474,11 +474,11 @@ static void dwmac4_set_sarc(struct dma_desc *p, u32 sarc_type) sarc_type)); } -static int set_16kib_bfsize (int mtu) +static int set_16kib_bfsize (int len) {int ret =0 ; -if (unlikely (mtu >= BUF_SIZE_8KiB)) +if (unlikely (len > BUF_SIZE_8KiB)) ret = BUF_SIZE_16KiB;return ret; }
drivers/net/ethernet/stmicro/stmmac/hwif.h +1 -1
@@ -540,7 +540,7 @@ struct stmmac_mode_ops {bool (*is_jumbo_frm)(unsigned int len,bool enh_desc);int (*jumbo_frm)(struct stmmac_tx_queue *tx_q,struct sk_buff *skb,int csum); -int (*set_16kib_bfsize)(int mtu); +int (*set_16kib_bfsize)(int len);void (*init_desc3)(struct dma_desc *p);void (*refill_desc3)(struct stmmac_rx_queue *rx_q,struct dma_desc *p);void (*clean_desc3)(struct stmmac_tx_queue *tx_q,struct dma_desc *p);
drivers/net/ethernet/stmicro/stmmac/ring_mode.c +2 -2
@@ -123,10 +123,10 @@ static void clean_desc3(struct stmmac_tx_queue *tx_q, struct dma_desc *p) p->des3 =0 ; } -static int set_16kib_bfsize (int mtu) +static int set_16kib_bfsize (int len) {int ret =0 ; -if (unlikely (mtu > BUF_SIZE_8KiB)) +if (unlikely (len > BUF_SIZE_8KiB)) ret = BUF_SIZE_16KiB;return ret; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 162/457 net: stmmac: size the RX buffers from the frame length, not the MTU
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Maxime Chevallier <[email protected]>
[ Upstream commit b8a26d46c0a4254f8bfe143681adb9d18d020299 ]
When picking the buffsize to use based on the MTU, we shouldn't check only the MTU value, but also :
- ETH_HLEN for the L2 header,
- up to 2 VLAN tags,
- the FCS,
The default bufsize is 1536 bytes, which is enough to contain all the above so this hasn't surfaced before, but the addition of NET_IP_ALIGN to the start of buffer address tripped the Jumbo selftest, leading to this discovery.
With that, we don't need the '>=' checks on the buffer len, we can use more consistent comparison operators in stmmac_set_bfsize.
--- .../net/ethernet/stmicro/stmmac/stmmac_main.c | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-)
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +11 -9
@@ -1536,17 +1536,17 @@ static unsigned int stmmac_rx_offset(struct stmmac_priv *priv)return NET_SKB_PAD +NET_IP_ALIGN ; } -static int stmmac_set_bfsize (int mtu) +static int stmmac_set_bfsize (int len) {int ret; -if (mtu >= BUF_SIZE_8KiB) +if (len > BUF_SIZE_8KiB) ret = BUF_SIZE_16KiB; -else if (mtu >= BUF_SIZE_4KiB) +else if (len > BUF_SIZE_4KiB) ret = BUF_SIZE_8KiB; -else if (mtu >= BUF_SIZE_2KiB) +else if (len > BUF_SIZE_2KiB) ret = BUF_SIZE_4KiB; -else if (mtu >DEFAULT_BUFSIZE ) +else if (len >DEFAULT_BUFSIZE ) ret = BUF_SIZE_2KiB;else ret =DEFAULT_BUFSIZE ;
@@ -4063,7 +4063,7 @@ static struct stmmac_dma_conf * stmmac_setup_dma_desc(struct stmmac_priv *priv, unsigned int mtu) {struct stmmac_dma_conf *dma_conf; -int bfsize, ret; +int bfsize, len, ret;u8 chan; dma_conf =kzalloc_obj (*dma_conf);
@@ -4073,13 +4073,15 @@ stmmac_setup_dma_desc(struct stmmac_priv *priv, unsigned int mtu)return ERR_PTR (-ENOMEM ); } -/* Returns 0 or BUF_SIZE_16KiB if mtu > 8KiB and dwmac4 or ring mode */ - bfsize =stmmac_set_16kib_bfsize (priv, mtu); + len = mtu +ETH_HLEN +2 *VLAN_HLEN +ETH_FCS_LEN ; + +/* Returns 0 or BUF_SIZE_16KiB if len > 8KiB and dwmac4 or ring mode */ + bfsize =stmmac_set_16kib_bfsize (priv, len);if (bfsize <0 ) bfsize =0 ;if (bfsize < BUF_SIZE_16KiB) - bfsize =stmmac_set_bfsize (mtu); + bfsize =stmmac_set_bfsize (len); dma_conf->dma_buf_sz = bfsize;/* Chose the tx/rx size from the already defined one in the
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 163/457 net: stmmac: selftests: Account for alignment shift on dwmac1000 for Jumbo test
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Maxime Chevallier <[email protected]>
[ Upstream commit c4ac6e94eb9423126bda907a7f2933284f7450ff ]
On dwmac1000, we currently only support single-descriptor frames. The Jumbo test started failing when NET_IP_ALIGN was added to align the IP header, as this tests tries to send the biggest possible frame.
On dwmac1000 the DMA transfer is aligned on 4-bytes, so adding a 2-byte shift at the start-of-buffer address means it takes a whole extra 4-byte DMA burst to receive the Jumbo packet, causing it to spill over the next descriptor.
This doesn't seem to happen on dwmac4 and xgmac that appear to correctly handle unaligned xfers (only tested on dwmac4)
Let's account for that in the Jumbo test, reduce the size of our big packet by the align size.
--- drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c | 3 +++ 1 file changed, 3 insertions(+)
drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c +3 -0
@@ -1809,6 +1809,9 @@ static int __stmmac_test_jumbo(struct stmmac_priv *priv, u16 queue)struct stmmac_packet_attrs attr = { };int size = priv->dma_conf.dma_buf_sz; +if (!dwmac_is_xmac (priv->plat->core_type)) + size -=NET_IP_ALIGN ; + attr.dst = priv->dev->dev_addr; attr.max_size = size -ETH_FCS_LEN ; attr.queue_mapping = queue;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 164/457 eth: fbnic: Avoid rounding zero ring sizes
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Björn Töpel <[email protected]>
[ Upstream commit 0160953d8eec75c3c55562158c46442ff1fd410b ]
roundup_pow_of_two() is undefined for zero. ethtool permits a zero ring size to reach the driver, where the minimum-size check should reject it.
Leave zero unchanged while rounding nonzero ring sizes. The minimum-size check then rejects zero deterministically without changing the established behavior for other values.
--- drivers/net/ethernet/meta/fbnic/fbnic_ethtool.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-)
drivers/net/ethernet/meta/fbnic/fbnic_ethtool.c +9 -4
@@ -313,6 +313,11 @@ fbnic_get_ringparam(struct net_device *netdev, struct ethtool_ringparam *ring, kernel_ring->hds_thresh = fbn->hds_thresh; } +static u32 fbnic_ring_size_pow2 (u32 size) +{ +return size ?roundup_pow_of_two (size) :0 ; +} +static void fbnic_set_rings (struct fbnic_net *fbn,struct ethtool_ringparam *ring,struct kernel_ethtool_ringparam *kernel_ring)
@@ -334,10 +339,10 @@ fbnic_set_ringparam(struct net_device *netdev, struct ethtool_ringparam *ring,struct fbnic_net *clone;int err; - ring->rx_pending =roundup_pow_of_two (ring->rx_pending); - ring->rx_mini_pending =roundup_pow_of_two (ring->rx_mini_pending); - ring->rx_jumbo_pending =roundup_pow_of_two (ring->rx_jumbo_pending); - ring->tx_pending =roundup_pow_of_two (ring->tx_pending); + ring->rx_pending =fbnic_ring_size_pow2 (ring->rx_pending); + ring->rx_mini_pending =fbnic_ring_size_pow2 (ring->rx_mini_pending); + ring->rx_jumbo_pending =fbnic_ring_size_pow2 (ring->rx_jumbo_pending); + ring->tx_pending =fbnic_ring_size_pow2 (ring->tx_pending);/* These are absolute minimums allowing the device and driver to operate * but not necessarily guarantee reasonable performance. Settings below
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 165/457 net/sched: fix potential stack infoleak in em_text_dump()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bernard Ladenthin <[email protected]>
[ Upstream commit 9c572a83037a7dcd653ba3a9cc468c16b857d0c9 ]
em_text_dump() allocates struct tcf_em_text on the stack without zeroing it. strscpy() writes the algorithm name and a NUL terminator into conf.algo[], leaving the remaining bytes uninitialised. nla_put_nohdr() then copies the full struct to the netlink response.
KMSAN on Linux 7.2-rc6 reports two kernel-infoleak splats from this path, one triggered via "tc filter show" and one via a raw RTM_GETTFILTER dump:
BUG: KMSAN: kernel-infoleak in _copy_to_iter+0x1c9/0x2620
nla_put_nohdr+0x83/0x130
em_text_dump+0x291/0x550
Local variable conf created at: em_text_dump+0x5d/0x550
Bytes 168-179 of 199 are uninitializedI am not certain whether this constitutes a real security problem in
practice: the test was conducted in a controlled KMSAN environment and the leaked stack bytes may or may not carry sensitive data on actual production kernels. I am reporting it because KMSAN flagged it as a kernel-infoleak and the fix is straightforward. I can provide a userspace reproducer on request.
The original code used strncpy() which zero-pads to the destination size. Commit b04202d6065c ("net/sched: replace strncpy with strscpy") replaced it with strscpy(), which does not pad, creating this condition. Zero-initialising the struct closes it.
--- net/sched/em_text.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/sched/em_text.c +1 -1
@@ -113,7 +113,7 @@ static void em_text_destroy(struct tcf_ematch *m)static int em_text_dump (struct sk_buff *skb,struct tcf_ematch *m) {struct text_match *tm =EM_TEXT_PRIV (m); -struct tcf_em_text conf; +struct tcf_em_text conf = {};strscpy (conf.algo, tm->config->ops->name); conf.from_offset = tm->from_offset;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 166/457 bpf: Reject dev-bound-only programs on other devices
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Weiming Shi <[email protected]>
[ Upstream commit 6db1ce73e9853f533eb7f413f14ba00f8ec6f80d ]
__bpf_offload_dev_match() falls back to comparing offdev pointers after an exact netdev mismatch. Bound-only programs normally have NULL offdevs, so unrelated netdevs compare equal. A bound-only program on an offload-registered netdev can instead inherit a real offdev and match a sibling port. With CAP_BPF and CAP_NET_ADMIN, a caller can use bpf(BPF_LINK_CREATE) with a different target ifindex to run metadata kfuncs specialized for the bound driver on the target driver's xdp_buff. Running a veth-bound program on tun reads beyond tun's bare stack xdp_buff as a veth_xdp_buff.
Oops: general protection fault, probably for non-canonical address KASAN: null-ptr-deref in range [0x0000000000000010-0x0000000000000017] RIP: 0010:veth_xdp_rx_timestamp (drivers/net/veth.c:1673) Call Trace: ... tun_build_skb (drivers/net/tun.c:1739) tun_get_user (drivers/net/tun.c:1856) tun_chr_write_iter (drivers/net/tun.c:2091) vfs_write (fs/read_write.c:595 fs/read_write.c:687) ksys_write (fs/read_write.c:739) do_syscall_64 (arch/x86/entry/syscall_64.c:84) entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121) Kernel panic - not syncing: Fatal exception in interrupt
Restrict non-offloaded programs to exact netdev matches and retain the shared-offdev fallback only for genuinely offloaded multi-port programs.
--- kernel/bpf/offload.c | 2 ++ 1 file changed, 2 insertions(+)
kernel/bpf/offload.c +2 -0
@@ -698,6 +698,8 @@ static bool __bpf_offload_dev_match(struct bpf_prog *prog,return false;if (offload->netdev == netdev)return true; +if (!bpf_prog_is_offloaded (prog->aux)) +return false; ondev1 =bpf_offload_find_netdev (offload->netdev); ondev2 =bpf_offload_find_netdev (netdev);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 167/457 nfc: nfcmrvl: validate helper command length before pull
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pengpeng Hou <[email protected]>
[ Upstream commit 686f942332b1667f13f3b8d6a2f50bcfbf42e277 ]
The firmware download receive path removes the NCI data header and reads the helper command before validating the remaining packet length. A short frame can therefore reach the data access before the malformed packet is rejected.
Validate the complete helper command length before stripping the NCI data header.
--- drivers/nfc/nfcmrvl/fw_dnld.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-)
drivers/nfc/nfcmrvl/fw_dnld.c +8 -3
@@ -263,9 +263,14 @@ static int process_state_fw_dnld(struct nfcmrvl_private *priv,* B8..N: payload */ -/* Remove NCI HDR */ -skb_pull (skb,3 ); -if (skb->data[0 ] !=HELPER_CMD_PACKET_FORMAT || skb->len !=5 ) { +if (skb->len !=NCI_DATA_HDR_SIZE +5 ) { +nfc_err (priv->dev,"bad command" ); +return -EINVAL ; + } + +/* Remove NCI header */ +skb_pull (skb,NCI_DATA_HDR_SIZE ); +if (skb->data[0 ] !=HELPER_CMD_PACKET_FORMAT ) {nfc_err (priv->dev,"bad command" );return -EINVAL ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 168/457 nfc: st21nfca: validate received frame size
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pengpeng Hou <[email protected]>
[ Upstream commit a653c01ce447f10c36b901646888c0330363af4f ]
st21nfca_hci_i2c_repack() trims a received frame at its EOF marker before removing byte stuffing. It then assumes the truncated frame contains the LLC header and two CRC bytes, and it unconditionally reads the byte after an escape marker.
A malformed frame can place EOF immediately after the start marker or can end its data portion with an escape marker. The former leaves too few bytes for check_crc(), while the latter makes the unstuffing loop read past the current skb length.
Require the minimum framing bytes both before and after unstuffing. Use separate input and output cursors while removing byte stuffing, and reject an escape marker without its encoded byte. This keeps malformed frames within the received frame boundary before CRC processing.
--- drivers/nfc/st21nfca/i2c.c | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-)
drivers/nfc/st21nfca/i2c.c +19 -10
@@ -289,27 +289,36 @@ static int check_crc(u8 *buf, int buflen)*/ static int st21nfca_hci_i2c_repack (struct sk_buff *skb) { -int i, j, r, size; +int read, write, r, size; -if (skb->len <1 || (skb->len >1 && skb->data[1 ] !=0 )) +if (skb->len <ST21NFCA_FRAME_HEADROOM || + !IS_START_OF_FRAME (skb->data))return -EBADMSG ; size =get_frame_size (skb->data, skb->len);if (size >0 ) { +if (size <ST21NFCA_FRAME_HEADROOM +2 ) +return -EBADMSG ; +skb_trim (skb, size);/* remove ST21NFCA byte stuffing for upper layer */ -for (i =1 , j =0 ; i < skb->len; i++) { -if (skb->data[i + j] == +for (read =1 , write =1 ; read < skb->len;) { +if (skb->data[read] == (u8 )ST21NFCA_ESCAPE_BYTE_STUFFING ) { - skb->data[i] = skb->data[i + j +1 ] - |ST21NFCA_BYTE_STUFFING_MASK ; - i++; - j++; +if (read +1 == skb->len) +return -EBADMSG ; + + skb->data[write++] = skb->data[read +1 ] + |ST21NFCA_BYTE_STUFFING_MASK ; + read +=2 ; + }else { + skb->data[write++] = skb->data[read++]; } - skb->data[i] = skb->data[i + j]; }/* remove byte stuffing useless byte */ -skb_trim (skb, i - j); +skb_trim (skb, write); +if (skb->len <ST21NFCA_FRAME_HEADROOM +2 ) +return -EBADMSG ;/* remove ST21NFCA_SOF_EOF from head */ skb_pull (skb,1 );
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 169/457 nfc: llcp: Fix list corruption / refcount desync in nfc_llcp_recv_dm()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Aldo Ariel Panzardo <[email protected]>
[ Upstream commit bf1460acdf8cf5a07c819f59785d40f20d113099 ]
nfc_llcp_recv_dm() handles DM(NOBOUND)/DM(REJ) for a socket that is still linked on local->connecting_sockets: it looks the socket up with nfc_llcp_connecting_sock_get(), sets sk->sk_state = LLCP_CLOSED and returns, without taking the socket lock and without unlinking the socket from the connecting_sockets list.
llcp_sock_release() selects the list to unlink from by sk_state: a socket in LLCP_CONNECTING is unlinked from connecting_sockets, otherwise from the sockets list. Because recv_dm left the socket physically on connecting_sockets but in the LLCP_CLOSED state, release() takes the else branch and calls nfc_llcp_sock_unlink(&local->sockets, sk). That runs sk_del_node_init() while holding sockets.lock, i.e. it removes the socket from the connecting_sockets hlist under the wrong lock. A concurrent connect() linking another socket onto connecting_sockets under connecting_sockets.lock then mutates the same hlist unserialized, which corrupts the list and desyncs the sk_add_node()/sk_del_node_init() sock_hold()/__sock_put() pairing. An unprivileged local process holding LLCP sockets, with the DM supplied by the remote peer over an established LLCP link, can drive this to leak kernel sockets without bound (the mis-decrement goes through the non-freeing __sock_put() path, so the object is never released), leading to memory exhaustion / DoS.
This is the same class of bug that was fixed in the sibling handler nfc_llcp_recv_cc() by commit b493ea2765cc ("nfc: llcp: Fix use-after-free race in nfc_llcp_recv_cc()"); recv_dm did not receive the equivalent fix.
Fix it the same way: take lock_sock(), re-check that the socket is still hashed (release() may have won the race), and for the NOBOUND/REJ case unlink it from connecting_sockets before moving it to LLCP_CLOSED. The unlink drops the connecting_sockets membership reference via sk_del_node_init(), leaving the socket unhashed, so the later nfc_llcp_sock_unlink() in llcp_sock_release() becomes a no-op and no double put occurs.
--- net/nfc/llcp_core.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+)
net/nfc/llcp_core.c +25 -0
@@ -1251,6 +1251,7 @@ static void nfc_llcp_recv_dm(struct nfc_llcp_local *local,struct nfc_llcp_sock *llcp_sock;struct sock *sk;u8 dsap, ssap, reason; +bool connecting = false; dsap =nfc_llcp_dsap (skb); ssap =nfc_llcp_ssap (skb);
@@ -1262,6 +1263,7 @@ static void nfc_llcp_recv_dm(struct nfc_llcp_local *local,case LLCP_DM_NOBOUND :case LLCP_DM_REJ : llcp_sock =nfc_llcp_connecting_sock_get (local, dsap); + connecting = true;break ;default :
@@ -1276,10 +1278,33 @@ static void nfc_llcp_recv_dm(struct nfc_llcp_local *local, sk = &llcp_sock->sk; +lock_sock (sk); + +/* Check if socket was destroyed whilst waiting for the lock */ +if (!sk_hashed (sk)) { +release_sock (sk); +nfc_llcp_sock_put (llcp_sock); +return ; + } + +/* +* For DM(NOBOUND)/DM(REJ) the socket is still linked on the +* connecting_sockets list. Unlink it here, under the socket lock, +* before moving it to LLCP_CLOSED: llcp_sock_release() selects the +* list to unlink from by sk_state, so leaving a connecting socket +* in the CLOSED state would make it unlink from the wrong list and +* corrupt the connecting_sockets list / desync the socket refcount. +* This mirrors nfc_llcp_recv_cc(). +*/ +if (connecting) +nfc_llcp_sock_unlink (&local->connecting_sockets, sk); + sk->sk_err =ENXIO ; sk->sk_state =LLCP_CLOSED ; sk->sk_state_change (sk); +release_sock (sk); +nfc_llcp_sock_put (llcp_sock); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 170/457 selftests: nci: Correct pthread_create return value check
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Lei Zhu <[email protected]>
[ Upstream commit 3d8afc5243ea2ee803d98e69eb4a01748167ac1b ]
The pthread_create() functions returns 0 on success and a positive value on failure. Modify the return value check to correctly detect failure cases.
--- tools/testing/selftests/nci/nci_dev.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-)
tools/testing/selftests/nci/nci_dev.c +7 -5
@@ -438,7 +438,7 @@ FIXTURE_SETUP(NCI)else rc =pthread_create (&thread_t,NULL , virtual_dev_open, (void *)&self->virtual_nci_fd); -ASSERT_GT (rc,-1 ); +ASSERT_EQ (rc,0 ); rc =send_cmd_with_idx (self->sd, self->fid, self->pid,NFC_CMD_DEV_UP , self->dev_idex);
@@ -509,7 +509,7 @@ FIXTURE_TEARDOWN(NCI) rc =pthread_create (&thread_t,NULL , virtual_deinit, (void *)&self->virtual_nci_fd); -ASSERT_GT (rc,-1 ); +ASSERT_EQ (rc,0 ); rc =send_cmd_with_idx (self->sd, self->fid, self->pid,NFC_CMD_DEV_DOWN , self->dev_idex);EXPECT_EQ (rc,0 );
@@ -590,7 +590,7 @@ int start_polling(int dev_idx, int proto, int virtual_fd, int sd, int fid, int p rc =pthread_create (&thread_t,NULL , virtual_poll_start, (void *)&virtual_fd); -if (rc <0 ) +if (rc)return rc; rc = send_cmd_mt_nla(sd, fid, pid, NFC_CMD_START_POLL, 2, nla_start_poll_type,
@@ -610,7 +610,7 @@ int stop_polling(int dev_idx, int virtual_fd, int sd, int fid, int pid) rc =pthread_create (&thread_t,NULL , virtual_poll_stop, (void *)&virtual_fd); -if (rc <0 ) +if (rc)return rc; rc = send_cmd_with_idx(sd, fid, pid,
@@ -830,6 +830,8 @@ int disconnect_tag(int nfc_sock, int virtual_fd) status =pthread_create (&thread_t,NULL , virtual_deactivate_proc, (void *)&virtual_fd); +if (status) +return status;close (nfc_sock);pthread_join (thread_t, (void **)&status);
@@ -874,7 +876,7 @@ TEST_F(NCI, deinit)else rc =pthread_create (&thread_t,NULL , virtual_deinit, (void *)&self->virtual_nci_fd); -ASSERT_GT (rc,-1 ); +ASSERT_EQ (rc,0 ); rc =send_cmd_with_idx (self->sd, self->fid, self->pid,NFC_CMD_DEV_DOWN , self->dev_idex);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 171/457 nfc: llcp: Fix race condition in accept_queue lifecycle
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Lee Jones <[email protected]>
[ Upstream commit c3eef2f988a3db9690369d7cef9a3344dd9788d3 ]
In nfc_llcp_socket_release(), sockets and listener accept queues are walked under the local sockets rwlock and bh_lock_sock(). However, bh_lock_sock() does not synchronise against process-context lock_sock() held by nfc_llcp_accept_dequeue() during accept(). Because socket_release() does not check sock_owned_by_user(), both paths can concurrently unlink and release the same child socket, resulting in use-after-free or a NULL pointer dereference of child->parent in nfc_llcp_accept_unlink().
Fix this synchronisation race by having nfc_llcp_socket_release() use process-context lock_sock() instead of bh_lock_sock():
1. Pop sockets from the local sockets list under the write lock using nfc_llcp_sock_list_pop() so lock_sock() can be acquired without holding the rwlock.
2. Because lock_sock() can sleep, defer the final release of the nfc_llcp_local structure to a workqueue (release_work). This avoids a sleeping-in-atomic bug when the last local reference is dropped from softirq context. Additionally, hold a single device reference on local from registration until final destruction.
3. In nfc_llcp_local_get(), use kref_get_unless_zero() to prevent resurrecting a local object whose teardown has been scheduled.
4. In llcp_sock_accept(), verify that the listener socket state is still LLCP_LISTEN after waking from schedule_timeout() to prevent hangs if the listener is closed concurrently.
5. When unlinking unaccepted child sockets during listener release, unlink them from local->sockets, call sock_orphan(), and drop their initial sk_alloc creation reference via sock_put().
6. Make nfc_llcp_accept_unlink() idempotent by guarding parent access with a NULL check.
--- net/nfc/llcp.h | 1 + net/nfc/llcp_core.c | 125 +++++++++++++++++++++++++++----------------- net/nfc/llcp_sock.c | 49 ++++++++++++----- 3 files changed, 116 insertions(+), 59 deletions(-)
net/nfc/llcp.h +1 -0
@@ -91,6 +91,7 @@ struct nfc_llcp_local {struct hlist_head pending_sdreqs;struct timer_list sdreq_timer;struct work_struct sdreq_timeout_work; +struct work_struct release_work;u8 sdreq_next_tid;/* sockets array */
net/nfc/llcp_core.c +78 -47
217 lines not on this page · raw message
net/nfc/llcp_sock.c +37 -12
87 lines not on this page · raw message
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 172/457 selftests: nci: Fix uninitialized family ID on missing attribute
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chaithanya Lagisetty <[email protected]>
[ Upstream commit eda518d2cdb6074a0bcdfa06af291616bcb5c421 ]
get_family_id() walks the generic netlink CTRL_CMD_GETFAMILY reply looking for the CTRL_ATTR_FAMILY_ID attribute and returns the parsed value in the local variable "id". If the reply does not carry that attribute, the parsing loop never assigns "id" and the function returns an indeterminate stack value, which the caller stores in self->fid and uses for subsequent netlink requests.
Initialize "id" to 0 so a missing attribute yields a deterministic (invalid) family ID instead of a garbage value.
--- tools/testing/selftests/nci/nci_dev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
tools/testing/selftests/nci/nci_dev.c +1 -1
@@ -182,7 +182,7 @@ static int get_family_id(int sd, __u32 pid, __u32 *event_group) } ans;struct nlattr *na;int resp_len; -__u16 id; +__u16 id =0 ;int len;int rc;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 173/457 selftests/nci: Fix out-of-bounds store on thread join
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chris Gellermann <[email protected]>
[ Upstream commit 6be581aeffc215bfc77939cd59902b0dbc4af23e ]
The NCI test collects the exit status of its helper threads by passing the address of an int to pthread_join():
int status; ... pthread_join(thread_t, (void **) &status);
pthread_join() stores a void pointer to the memory location. On 64-bit systems, a void pointer is wider than an int, so the store overruns the 4 bytes of space allocated on the stack for the integer and corrupts the adjacent stack. On our CHERI system, this caused a fault due to a capability bounds violation.
Fix this by introducing a helper that joins a thread through a void pointer and converts the result back to an integer, which is what the helper threads return.
While here, also fix the logic in disconnect_tag() if the helper thread creation failed. Previously, it would have joined a thread that was never created when pthread_create() failed.
--- tools/testing/selftests/nci/nci_dev.c | 31 +++++++++++++++++---------- 1 file changed, 20 insertions(+), 11 deletions(-)
tools/testing/selftests/nci/nci_dev.c +20 -11
@@ -8,6 +8,7 @@#include <stdlib.h> #include <errno.h> +#include <stdint.h> #include <string.h> #include <sys/ioctl.h> #include <fcntl.h>
@@ -87,6 +88,16 @@ struct msgtemplate {char buf[MAX_MSG_SIZE ]; }; +static int join_thread_status (pthread_t thread) +{ +void *thread_ret =NULL ; + +if (pthread_join (thread, &thread_ret)) +return -1 ; + +return (int )(intptr_t )thread_ret; +} +static int create_nl_socket (void ) {int fd;
@@ -444,7 +455,7 @@ FIXTURE_SETUP(NCI) NFC_CMD_DEV_UP, self->dev_idex);EXPECT_EQ (rc,0 ); -pthread_join (thread_t, (void **)&status); + status =join_thread_status (thread_t);ASSERT_EQ (status,0 ); self->open_state = true; }
@@ -514,7 +525,7 @@ FIXTURE_TEARDOWN(NCI) NFC_CMD_DEV_DOWN, self->dev_idex);EXPECT_EQ (rc,0 ); -pthread_join (thread_t, (void **)&status); + status =join_thread_status (thread_t);ASSERT_EQ (status,0 ); }
@@ -585,7 +596,6 @@ int start_polling(int dev_idx, int proto, int virtual_fd, int sd, int fid, int pvoid *nla_start_poll_data[2 ] = {&dev_idx, &proto};int nla_start_poll_len[2 ] = {4 ,4 };pthread_t thread_t; -int status;int rc; rc = pthread_create(&thread_t, NULL, virtual_poll_start,
@@ -598,14 +608,12 @@ int start_polling(int dev_idx, int proto, int virtual_fd, int sd, int fid, int pif (rc !=0 )return rc; -pthread_join (thread_t, (void **)&status); -return status; +return join_thread_status (thread_t); }int stop_polling (int dev_idx,int virtual_fd,int sd,int fid,int pid) {pthread_t thread_t; -int status;int rc; rc = pthread_create(&thread_t, NULL, virtual_poll_stop,
@@ -618,8 +626,7 @@ int stop_polling(int dev_idx, int virtual_fd, int sd, int fid, int pid)if (rc !=0 )return rc; -pthread_join (thread_t, (void **)&status); -return status; +return join_thread_status (thread_t); }TEST_F (NCI , start_poll)
@@ -834,8 +841,10 @@ int disconnect_tag(int nfc_sock, int virtual_fd)return status;close (nfc_sock); -pthread_join (thread_t, (void **)&status); -return status; +if (status) +return -1 ; + +return join_thread_status (thread_t); }TEST_F (NCI , t4t_tag_read)
@@ -882,7 +891,7 @@ TEST_F(NCI, deinit) NFC_CMD_DEV_DOWN, self->dev_idex);EXPECT_EQ (rc,0 ); -pthread_join (thread_t, (void **)&status); + status =join_thread_status (thread_t); self->open_state =0 ;ASSERT_EQ (status,0 );
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 174/457 nfc: virtual_ncidev: Add missing ioctl compat handler
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chris Gellermann <[email protected]>
[ Upstream commit 51814683e28fc64eceb415962376956c3cfc75a7 ]
The compat handler for ioctls to the virtual nci device is missing. So, nci-specific ioctls of a compat task return with -1 and errno set to ENOTTY. Add a handler.
The handling of an ioctl() call of a compat task to get the index of virtual nci device (IOCTL_GET_NCIDEV_IDX) lands in the default case of the ioctl compat handler (see fs/ioctl.c):
COMPAT_SYSCALL_DEFINE3(ioctl, ...)
{
...
default:
error = do_vfs_ioctl(fd_file(f), fd, cmd, ...);
if (error != -ENOIOCTLCMD)
break;
if (fd_file(f)->f_op->compat_ioctl)
error = fd_file(f)->f_op->compat_ioctl(fd_file(f), cmd, arg);
if (error == -ENOIOCTLCMD)
error = -ENOTTY;
...
}There, do_vfs_ioctl() returns -ENOIOCTLCMD and compat_ioctl is not set for virtual_ncidev_fops, i.e. f_op->compat_ioctl == NULL. So, the ioctl() syscall returns with -1 and errno set to ENOTTY to the compat task.
To fix this, use the compat_ptr_ioctl helper for compat handling here. It shall be used for ioctls that "either ignore the argument or pass a pointer to a compatible data type". The driver's sole ioctl takes a user void pointer and copies nfc_dev->idx to it, a 4-byte integer across all ABIs.
This issue has been found by running the nci_dev kernel selftest as rv64 binary on top of a CHERI kernel, where the ioctl() ends up in the ioctl compat handler, similar to a 32-bit application on top of a 64-bit kernel.
--- drivers/nfc/virtual_ncidev.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/nfc/virtual_ncidev.c +2 -1
@@ -195,7 +195,8 @@ static const struct file_operations virtual_ncidev_fops = { .write = virtual_ncidev_write, .open = virtual_ncidev_open, .release = virtual_ncidev_close, - .unlocked_ioctl = virtual_ncidev_ioctl + .unlocked_ioctl = virtual_ncidev_ioctl, + .compat_ioctl = compat_ptr_ioctl, };static struct miscdevice miscdev = {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 175/457 nfc: llcp: fix sdreq TLV list leak on parse/alloc/send failure
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Cong Nguyen <[email protected]>
[ Upstream commit 66f4300206b82b0b143ef0d9be90cd8d29f23a47 ]
nfc_genl_llc_sdreq() builds a list of TLV nodes while walking nested netlink attrs, but 3 error paths (nested-attr parse failure, TLV alloc ENOMEM, nfc_llcp_send_snl_sdreq() failure) all skip freeing what was already queued.
Route them through a new free_list label, mirroring the SDRES path in the same file which already does this. Harmless on the success path too -- send_snl_sdreq() drains the list as it moves nodes, so it's already empty by the time free_list runs.
--- net/nfc/netlink.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-)
net/nfc/netlink.c +5 -2
@@ -1181,7 +1181,7 @@ static int nfc_genl_llc_sdreq(struct sk_buff *skb, struct genl_info *info)if (rc !=0 ) { rc = -EINVAL ; - goto put_local; + goto free_list; }if (!sdp_attrs[NFC_SDP_ATTR_URI ])
@@ -1200,7 +1200,7 @@ static int nfc_genl_llc_sdreq(struct sk_buff *skb, struct genl_info *info) sdreq =nfc_llcp_build_sdreq_tlv (tid, uri, uri_len);if (sdreq ==NULL ) { rc = -ENOMEM ; - goto put_local; + goto free_list; } tlvs_len += sdreq->tlv_len;
@@ -1215,6 +1215,9 @@ static int nfc_genl_llc_sdreq(struct sk_buff *skb, struct genl_info *info) rc =nfc_llcp_send_snl_sdreq (local, &sdreq_list, tlvs_len); +free_list: +nfc_llcp_free_sdp_tlv_list (&sdreq_list); + put_local:nfc_llcp_local_put (local);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 176/457 nfc: st21nfca: validate ISO15693 inventory length
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pengpeng Hou <[email protected]>
[ Upstream commit 7f2ea5ed588c03d481f0301e6c3d4240132383fb ]
The ISO15693 inventory helper removes a two-byte prefix without checking that it exists, then accepts a one-byte remainder before reading data[1] as the DSFID.
Require the prefix and at least two remaining bytes before copying the UID data and reading the DSFID.
--- drivers/nfc/st21nfca/core.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-)
drivers/nfc/st21nfca/core.c +1 -3
@@ -577,9 +577,7 @@ static int st21nfca_get_iso15693_inventory(struct nfc_hci_dev *hdev,if (r <0 ) goto exit; -skb_pull (inventory_skb,2 ); - -if (inventory_skb->len ==0 || +if (!skb_pull (inventory_skb,2 ) || inventory_skb->len <2 || inventory_skb->len >NFC_ISO15693_UID_MAXSIZE ) { r = -EPROTO ; goto exit;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 177/457 nfc: llcp: fix -ENOMEM on connect with zero-length service name
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ömer Mete Kaya <[email protected]>
[ Upstream commit c04981e42d94f39c1dba965cc462a046e946a6c5 ]
When service_name_len is 0, kmemdup() returns ZERO_SIZE_PTR which passes the NULL check, causing nfc_llcp_send_connect() to attempt building a zero-length service name TLV and fail with -ENOMEM.
Fix by setting service_name to NULL directly when service_name_len is 0.
--- net/nfc/llcp_sock.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-)
net/nfc/llcp_sock.c +10 -6
@@ -759,12 +759,16 @@ static int llcp_sock_connect(struct socket *sock, struct sockaddr_unsized *_addr llcp_sock->service_name_len = min_t(unsigned int, addr->service_name_len,NFC_LLCP_MAX_SERVICE_NAME ); - llcp_sock->service_name =kmemdup (addr->service_name, - llcp_sock->service_name_len, -GFP_KERNEL ); -if (!llcp_sock->service_name) { - ret = -ENOMEM ; - goto sock_llcp_release; +if (llcp_sock->service_name_len ==0 ) { + llcp_sock->service_name =NULL ; + }else { + llcp_sock->service_name =kmemdup (addr->service_name, + llcp_sock->service_name_len, +GFP_KERNEL ); +if (!llcp_sock->service_name) { + ret = -ENOMEM ; + goto sock_llcp_release; + } }nfc_llcp_sock_link (&local->connecting_sockets, sk);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 178/457 nfc: llcp: fix WKS SAP hijacking via prefix match in nfc_llcp_wks_sap()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ömer Mete Kaya <[email protected]>
[ Upstream commit 408cff6bd60636df201274d320edfdfde9ed41db ]
nfc_llcp_wks_sap() compares only service_name_len bytes, so a short service_name like "u" matches longer WKS strings like "urn:nfc:sn:snep". Fix by requiring exact length match before strncmp().
--- net/nfc/llcp_core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
net/nfc/llcp_core.c +2 -1
@@ -369,7 +369,8 @@ static int nfc_llcp_wks_sap(const char *service_name, size_t service_name_len)if (wks[sap] ==NULL )continue ; -if (strncmp (wks[sap], service_name, service_name_len) ==0 ) +if (strlen (wks[sap]) == service_name_len && + !strncmp (wks[sap], service_name, service_name_len))return sap; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 179/457 nfc: llcp: fix slab-out-of-bounds reads when logging service names
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ömer Mete Kaya <[email protected]>
[ Upstream commit 7dcf371a35632f035baf77bcf2c129165f772ce4 ]
nfc_llcp_wks_sap() and nfc_llcp_build_sdreq_tlv() pass non-null- terminated strings to pr_debug() using the %s format specifier. The buffers are allocated via kmemdup() or come from netlink attributes and are not guaranteed to be null-terminated, causing __dynamic_pr_debug() to read beyond the allocated region:
KASAN: slab-out-of-bounds Read in __dynamic_pr_debug
Fix both call sites by using %.*s with the explicit length to limit the output to the actual length of the string.
--- net/nfc/llcp_commands.c | 2 +- net/nfc/llcp_core.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-)
net/nfc/llcp_commands.c +1 -1
@@ -135,7 +135,7 @@ struct nfc_llcp_sdp_tlv *nfc_llcp_build_sdreq_tlv(u8 tid, const char *uri, {struct nfc_llcp_sdp_tlv *sdreq; -pr_debug ("uri: %s, len: %zu\n" , uri, uri_len); +pr_debug ("uri: %.*s, len: %zu\n" , (int )uri_len, uri, uri_len);/* sdreq->tlv_len is u8, takes uri_len, + 3 for header, + 1 for NULL */ if (WARN_ON_ONCE (uri_len >U8_MAX -4 ))
net/nfc/llcp_core.c +1 -1
@@ -358,7 +358,7 @@ static int nfc_llcp_wks_sap(const char *service_name, size_t service_name_len) {int sap, num_wks; -pr_debug ("%s\n" , service_name); +pr_debug ("%.*s\n" , (int )service_name_len, service_name);if (service_name ==NULL )return -EINVAL ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 180/457 nfc: pn533: fix OOB read in pn533_acr122_is_rx_frame_valid()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Deepanshu Kartikey <[email protected]>
[ Upstream commit b61732f47316d45f27706db7812950145d3327b5 ]
frame->ccid.datalen is read directly from the USB response frame and used, unchecked, as an index into frame->data[]. A malicious or malfunctioning device can set this field to an arbitrary value, causing the driver to read far outside the received buffer.
Bound ccid.datalen against the maximum possible ACR122 frame size before using it. This replaces the existing datalen == 0 check, since datalen < 2 already covers that case and additionally rejects datalen == 1, which would still underflow the "datalen - 2" offset used below.
--- drivers/nfc/pn533/usb.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/nfc/pn533/usb.c +3 -1
@@ -319,7 +319,9 @@ static bool pn533_acr122_is_rx_frame_valid(void *_frame, struct pn533 *dev)if (frame->ccid.type !=0x83 )return false; -if (!frame->ccid.datalen) +if (frame->ccid.datalen <2 || + frame->ccid.datalen >PN533_ACR122_FRAME_MAX_PAYLOAD_LEN + +PN533_ACR122_RX_FRAME_TAIL_LEN )return false;if (frame->data[frame->ccid.datalen -2 ] ==0x63 )
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 181/457 net/sched: act_gate: budget the per-entry list in get_fill_size
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Victor Nogueira <[email protected]>
[ Upstream commit cfa165cbfbed9d0f4bbc22fef4309f595a3ab187 ]
tcf_gate_get_fill_size returns only the TCA_GATE_PARMS size, but tcf_gate_dump also emits three 64-bit timestamps, the clock id, flags, priority and the variable-length TCA_GATE_ENTRY_LIST nest. The per-entry nest is unbounded: parse_gate_list places no cap on the number of sched-entries, so a gate with many entries can push the real dump well past the skb that tca_get_fill allocates from this size.
RTM_NEWACTION then fails the add-notify with -EINVAL while the action is already committed to the IDR, and a subsequent RTM_GETACTION on the installed gate also returns -EINVAL because its dump no longer fits.
Fix this by accounting for the missing fields in tcf_gate_get_fill_size along with all elements in the entries list.
Note that sizing the reply from the action lets an oversized gate install cleanly for the first time: with the input unbounded by parse_gate_list, the sized skb can now grow well above NLMSG_GOODSIZE per netlink request (a transient GFP_KERNEL allocation reachable only with namespace-local CAP_NET_ADMIN). Overload from a malicious netns admin is hardening material, not net, per the discussion at
https://lore.kernel.org/netdev/[email protected]/;
a follow-up patch for net-next will cap the sched-entry count.
--- net/sched/act_gate.c | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-)
net/sched/act_gate.c +29 -1
@@ -681,7 +681,35 @@ static void tcf_gate_stats_update(struct tc_action *a, u64 bytes, u64 packets,static size_t tcf_gate_get_fill_size (const struct tc_action *act) { -return nla_total_size (sizeof (struct tc_gate )); +struct tcf_gate *gact =to_gate (act); +const struct tcf_gate_params *p; +struct tcfg_gate_entry *entry; +size_t size =nla_total_size (sizeof (struct tc_gate ))/* TCA_GATE_PARMS */ + +3 *nla_total_size_64bit (sizeof (u64))/* TCA_GATE_BASE_TIME +* TCA_GATE_CYCLE_TIME +* TCA_GATE_CYCLE_TIME_EXT +*/ + +nla_total_size (sizeof (s32))/* TCA_GATE_CLOCKID */ + +nla_total_size (sizeof (u32))/* TCA_GATE_FLAGS */ + +nla_total_size (sizeof (s32))/* TCA_GATE_PRIORITY */ + +nla_total_size (0 );/* TCA_GATE_ENTRY_LIST */ +/* TCA_GATE_TM is budgeted by tcf_action_shared_attrs_size() */ + +rcu_read_lock (); + p =rcu_dereference (gact->param); +if (p) { +list_for_each_entry_rcu (entry, &p->entries, list) +/* TCA_GATE_ONE_ENTRY nest and its attributes */ + size +=nla_total_size (0 ) + +nla_total_size (sizeof (u32))/* TCA_GATE_ENTRY_INDEX */ + +nla_total_size (0 )/* TCA_GATE_ENTRY_GATE */ + +nla_total_size (sizeof (u32))/* TCA_GATE_ENTRY_INTERVAL */ + +nla_total_size (sizeof (s32))/* TCA_GATE_ENTRY_MAX_OCTETS */ + +nla_total_size (sizeof (s32));/* TCA_GATE_ENTRY_IPV */ + } +rcu_read_unlock (); + +return size; }static void tcf_gate_entry_destructor (void *priv)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 182/457 net: bcmgenet: stop Tx NAPI before disabling the queues
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Nicolai Buchwitz <[email protected]>
[ Upstream commit 7e87508b5c4d81210d0a736ed01962e52f5c4c56 ]
bcmgenet_netif_stop() and the Wake-on-LAN branch of bcmgenet_suspend() both disable the Tx queues first and stop Tx NAPI several steps later. A completion in flight calls netif_tx_wake_queue() in between, and nothing stops the queue again, so a transmit can reach the rings after they have been freed.
Close is safe because dev_deactivate_many() stops the qdisc first. bcmgenet_suspend() does not, so stop Tx NAPI before the queues on both paths.
KASAN on a Raspberry Pi CM4, driven from an MTU change because suspend freezes user space before the callback runs:
BUG: KASAN: use-after-free in bcmgenet_xmit+0x17f8/0x2258 Write of size 8 at addr ffffff8055844a68 by task ksoftirqd/0/14 bcmgenet_xmit+0x17f8/0x2258 dev_hard_start_xmit+0x13c/0x588 sch_direct_xmit+0x108/0x340 __dev_queue_xmit+0x1190/0x3848
--- drivers/net/ethernet/broadcom/genet/bcmgenet.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
drivers/net/ethernet/broadcom/genet/bcmgenet.c +4 -2
@@ -3441,6 +3441,8 @@ static void bcmgenet_netif_stop(struct net_device *dev, bool stop_phy) {struct bcmgenet_priv *priv =netdev_priv (dev); +/* Stop completion polling before it can wake a stopped queue */ +bcmgenet_disable_tx_napi (priv);netif_tx_disable (dev);/* Disable MAC receive */
@@ -3455,7 +3457,6 @@ static void bcmgenet_netif_stop(struct net_device *dev, bool stop_phy)/* Disable MAC transmit. TX DMA disabled must be done before this */ umac_enable_set (priv,CMD_TX_EN , false); -bcmgenet_disable_tx_napi (priv);bcmgenet_disable_rx_napi (priv);bcmgenet_intr_disable (priv);
@@ -4320,6 +4321,8 @@ static int bcmgenet_suspend(struct device *d)netif_device_detach (dev);if (device_may_wakeup (d) && priv->wolopts) { +/* Stop completion polling before it can wake a stopped queue */ +bcmgenet_disable_tx_napi (priv);netif_tx_disable (dev);/* Suspend non-wake Rx data flows */
@@ -4348,7 +4351,6 @@ static int bcmgenet_suspend(struct device *d)netdev_warn (priv->dev,"Timed out while disabling TX DMA\n" ); -bcmgenet_disable_tx_napi (priv);bcmgenet_disable_rx_napi (priv);disable_irq (priv->irq1);bcmgenet_tx_reclaim_all (dev);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 183/457 veth: manage XDP program pointers during channel resize
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jakub Kicinski <[email protected]>
[ Upstream commit 7104a370714346b667712913dc16abf14bbc97ed ]
veth_set_channels() tears down XDP resources for removed RX queues without clearing rq->xdp_prog. If the program is then detached or replaced, those queues keep the old pointer after bpf_prog_put(). A later channel increase can re-enable NAPI and run the freed program.
BUG: unable to handle page fault for address: ffffc90000256048
Oops: Oops: 0000 [#1] SMP KASAN NOPTI
RIP: veth_xdp_rcv_skb (include/linux/filter.h:779
include/net/xdp.h:696 drivers/net/veth.c:820)
Call Trace:
veth_xdp_rcv (drivers/net/veth.c:941)
veth_poll (drivers/net/veth.c:986)
__napi_poll (net/core/dev.c:7787)
net_rx_action (net/core/dev.c:7850 net/core/dev.c:8007)
handle_softirqs (kernel/softirq.c:645)
Kernel panic - not syncing: Fatal exception in interrupt--- drivers/net/veth.c | 2 ++ 1 file changed, 2 insertions(+)
drivers/net/veth.c +2 -0
@@ -1053,6 +1053,7 @@ static int __veth_napi_enable_range(struct net_device *dev, int start, int end)for (i = start; i < end; i++) {struct veth_rq *rq = &priv->rq[i]; +rcu_assign_pointer (rq->xdp_prog, priv->_xdp_prog);napi_enable (&rq->xdp_napi);rcu_assign_pointer (priv->rq[i].napi, &priv->rq[i].xdp_napi); }
@@ -1087,6 +1088,7 @@ static void veth_napi_del_range(struct net_device *dev, int start, int end)rcu_assign_pointer (priv->rq[i].napi,NULL );napi_disable (&rq->xdp_napi); +rcu_assign_pointer (rq->xdp_prog,NULL );__netif_napi_del (&rq->xdp_napi); }synchronize_net ();
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 184/457 net: skbuff: fix pull-bound underflow in skb_checksum_setup_ipv6()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Shihuang Liu <[email protected]>
[ Upstream commit 3b4e0b0c008a8c1b474730248cd5b873026c74bd ]
skb_maybe_pull_tail() subtracts skb_headlen(skb) from the unsigned max argument and passes the result to __pskb_pull_tail() as a signed int. The function does not ensure that max is at least skb_headlen(skb).
This can happen while parsing IPv6 extension headers when an skb already has a linear area larger than MAX_IPV6_HDR_LEN. Once the parser needs data beyond the linear area, max - skb_headlen(skb) wraps and is converted to a negative delta. __pskb_pull_tail() then passes that negative length to skb_copy_bits(), where it can become a very large copy length.
Pass the requested length itself as the pull bound at the three extension-header call sites, so the delta can no longer go negative.
--- net/core/skbuff.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-)
net/core/skbuff.c +6 -3
@@ -5965,7 +5965,8 @@ static int skb_checksum_setup_ipv6(struct sk_buff *skb, bool recalculate) err =skb_maybe_pull_tail (skb, off +sizeof (struct ipv6_opt_hdr ), -MAX_IPV6_HDR_LEN ); + off + +sizeof (struct ipv6_opt_hdr ));if (err <0 ) goto out;
@@ -5980,7 +5981,8 @@ static int skb_checksum_setup_ipv6(struct sk_buff *skb, bool recalculate) err =skb_maybe_pull_tail (skb, off +sizeof (struct ip_auth_hdr ), -MAX_IPV6_HDR_LEN ); + off + +sizeof (struct ip_auth_hdr ));if (err <0 ) goto out;
@@ -5995,7 +5997,8 @@ static int skb_checksum_setup_ipv6(struct sk_buff *skb, bool recalculate) err =skb_maybe_pull_tail (skb, off +sizeof (struct frag_hdr ), -MAX_IPV6_HDR_LEN ); + off + +sizeof (struct frag_hdr ));if (err <0 ) goto out;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 185/457 vrf: Stop corrupting skb->csum when capturing CHECKSUM_COMPLETE packets
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ido Schimmel <[email protected]>
[ Upstream commit ab7aa05c06ae340e5c7530bb78fa8d23794e460b ]
The VRF device is an Ethernet device but it can have non-Ethernet ports such as IP tunnels. Before the cited commit, capturing packets from such ports on the VRF device resulted in these packets being detected as malformed since they lack an Ethernet header.
The cited commit fixed it by pushing a dummy Ethernet header to such packets before the capture and pulling it afterwards. In the case of CHECKSUM_COMPLETE packets it also updated skb->csum with the checksum of the dummy Ethernet header. This is wrong as skb->csum should not include the checksum of the Ethernet header ("checksum of the _whole_ packet as seen by netif_rx()").
This also means that L4 protocols receive a corrupted skb->csum and potentially drop the packet, as is the case with UDP packets whose checksum was completed by software.
Fix by removing the unnecessary call to skb_postpush_rcsum().
--- drivers/net/vrf.c | 2 -- 1 file changed, 2 deletions(-)
drivers/net/vrf.c +0 -2
@@ -1175,8 +1175,6 @@ static int vrf_prepare_mac_header(struct sk_buff *skb, skb->protocol = eth->h_proto; skb->pkt_type =PACKET_HOST ; -skb_postpush_rcsum (skb, skb->data,ETH_HLEN ); -skb_pull_inline (skb,ETH_HLEN );return 0 ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 186/457 net: dsa: mv88e6xxx: 88E6191X and 88E6193X have no PTP
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Nicolo Giuliani <[email protected]>
[ Upstream commit 6b491af01aa5c0633a580e3b11bc7277adc903b2 ]
The 88E6191X and 88E6193X are 6393 family devices that share mv88e6393x_ops with the 88E6393X and are marked as ptp_support. Marvell's UMSD driver describes both as parts without AVB (88E6193X: "BGA package - No AVB, No Routing, No Cut-through"), and the register access confirms it on an 88E6193X: the whole indirect AVB register space behind Global 2 registers 0x16 and 0x17 reads zero, for every port, block and address, with the 6390 and with the 6352 command encoding. Writes to the TAI registers, including the clock period register and the TAI global configuration register, read back as zero.
Since commit 7e3c18097a70 ("net: dsa: mv88e6xxx: read cycle counter period from hardware") the PTP setup reads the TAI clock period, so the switch fails to probe:
mv88e6xxx ...: unexpected cycle counter period of 0 ps
Add mv88e6191x_ops, a copy of mv88e6393x_ops without avb_ops and ptp_ops, use it for the 88E6191X and the 88E6193X and stop setting ptp_support for them. The 88E6393X is unchanged.
Tested on an 88E6193X (Sophos XGS 107w): the switch probes and the ports work. I do not have an 88E6191X, it is changed because UMSD describes it the same way.
--- drivers/net/dsa/mv88e6xxx/chip.c | 68 ++++++++++++++++++++++++++++++-- 1 file changed, 64 insertions(+), 4 deletions(-)
drivers/net/dsa/mv88e6xxx/chip.c +64 -4
@@ -5639,6 +5639,68 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = { .pcs_ops = &mv88e6390_pcs_ops, }; +static const struct mv88e6xxx_ops mv88e6191x_ops = { +/* MV88E6XXX_FAMILY_6393 without AVB and PTP: 6191X and 6193X */ + .irl_init_all = mv88e6390_g2_irl_init_all, + .get_eeprom = mv88e6xxx_g2_get_eeprom8, + .set_eeprom = mv88e6xxx_g2_set_eeprom8, + .set_switch_mac = mv88e6xxx_g2_set_switch_mac, + .phy_read = mv88e6xxx_g2_smi_phy_read_c22, + .phy_write = mv88e6xxx_g2_smi_phy_write_c22, + .phy_read_c45 = mv88e6xxx_g2_smi_phy_read_c45, + .phy_write_c45 = mv88e6xxx_g2_smi_phy_write_c45, + .port_set_link = mv88e6xxx_port_set_link, + .port_sync_link = mv88e6xxx_port_sync_link, + .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, + .port_set_speed_duplex = mv88e6393x_port_set_speed_duplex, + .port_tag_remap = mv88e6390_port_tag_remap, + .port_set_policy = mv88e6393x_port_set_policy, + .port_set_frame_mode = mv88e6351_port_set_frame_mode, + .port_set_ucast_flood = mv88e6352_port_set_ucast_flood, + .port_set_mcast_flood = mv88e6352_port_set_mcast_flood, + .port_set_ether_type = mv88e6393x_port_set_ether_type, + .port_set_jumbo_size = mv88e6165_port_set_jumbo_size, + .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, + .port_pause_limit = mv88e6390_port_pause_limit, + .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, + .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, + .port_get_cmode = mv88e6352_port_get_cmode, + .port_set_cmode = mv88e6393x_port_set_cmode, + .port_setup_message_port = mv88e6xxx_setup_message_port, + .port_set_upstream_port = mv88e6393x_port_set_upstream_port, + .port_enable_tcam = mv88e6xxx_port_enable_tcam, + .stats_snapshot = mv88e6390_g1_stats_snapshot, + .stats_set_histogram = mv88e6390_g1_stats_set_histogram, + .stats_get_sset_count = mv88e6320_stats_get_sset_count, + .stats_get_strings = mv88e6320_stats_get_strings, + .stats_get_stat = mv88e6390_stats_get_stat, +/* .set_cpu_port is missing because this family does not support a global +* CPU port, only per port CPU port which is set via +* .port_set_upstream_port method. +*/ + .set_egress_port = mv88e6393x_set_egress_port, + .watchdog_ops = &mv88e6393x_watchdog_ops, + .mgmt_rsvd2cpu = mv88e6393x_port_mgmt_rsvd2cpu, + .pot_clear = mv88e6xxx_g2_pot_clear, + .hardware_reset_pre = mv88e6xxx_g2_eeprom_wait, + .hardware_reset_post = mv88e6xxx_g2_eeprom_wait, + .reset = mv88e6352_g1_reset, + .rmu_disable = mv88e6390_g1_rmu_disable, + .atu_get_hash = mv88e6165_g1_atu_get_hash, + .atu_set_hash = mv88e6165_g1_atu_set_hash, + .vtu_getnext = mv88e6390_g1_vtu_getnext, + .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, + .stu_getnext = mv88e6390_g1_stu_getnext, + .stu_loadpurge = mv88e6390_g1_stu_loadpurge, + .serdes_get_lane = mv88e6393x_serdes_get_lane, + .serdes_irq_mapping = mv88e6390_serdes_irq_mapping, +/* TODO: serdes stats */ + .gpio_ops = &mv88e6352_gpio_ops, + .phylink_get_caps = mv88e6393x_phylink_get_caps, + .pcs_ops = &mv88e6393x_pcs_ops, + .tcam_ops = &mv88e6393_tcam_ops, +}; +static const struct mv88e6xxx_ops mv88e6393x_ops = {/* MV88E6XXX_FAMILY_6393 */ .irl_init_all = mv88e6390_g2_irl_init_all,
@@ -6163,8 +6225,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .atu_move_port_mask = 0x1f, .pvt = true, .multi_chip = true, - .ptp_support = true, - .ops = &mv88e6393x_ops, + .ops = &mv88e6191x_ops, }, [MV88E6193X] = {
@@ -6190,8 +6251,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .atu_move_port_mask = 0x1f, .pvt = true, .multi_chip = true, - .ptp_support = true, - .ops = &mv88e6393x_ops, + .ops = &mv88e6191x_ops, }, [MV88E6220] = {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 187/457 net: mdio: realtek-rtl9300: fix RTL931x C22 extended page selection
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jonas Jelonek <[email protected]>
[ Upstream commit 89a8a1eef2d441b7825a6c0116ce817235a7ffe6 ]
The RTL931x indirect access engine has a separate nine-bit extended page field. The driver leaves it at zero, and otto_emdio_run_cmd() therefore programs extended page zero for every Clause 22 transaction. This overrides page selection made through PHY register 30, causing accesses to private PHY pages to hit extended page zero instead.
Set the field to its 0x1ff "do not change" value for RTL931x Clause 22 reads and writes. This preserves extended page selection made through PHY register 30 and restores access to its private register pages.
--- drivers/net/mdio/mdio-realtek-rtl9300.c | 6 ++++++ 1 file changed, 6 insertions(+)
drivers/net/mdio/mdio-realtek-rtl9300.c +6 -0
@@ -88,6 +88,8 @@#define RTL9310_SMI_INDRT_ACCESS_BC_PHYID_CTRL 0x0c14#define RTL9310_BC_PORT_ID GENMASK(10, 5)#define RTL9310_SMI_INDRT_ACCESS_CTRL_1 0x0c04 +#define RTL9310_SMI_INDRT_EXT_PAGE GENMASK(8, 0) +#define RTL9310_SMI_INDRT_EXT_PAGE_NO_CHANGE 0x1ff#define RTL9310_SMI_INDRT_ACCESS_CTRL_2_LOW 0x0c08#define RTL9310_SMI_INDRT_ACCESS_CTRL_2_HIGH 0x0c0c#define RTL9310_SMI_INDRT_ACCESS_CTRL_3 0x0c10/* I/O fields flipped */
@@ -325,6 +327,8 @@ static int otto_emdio_9310_read_c22(struct mii_bus *bus, int port, int regnum, u .broadcast = FIELD_PREP(RTL9310_BC_PORT_ID, port), .c22_data =FIELD_PREP (RTL9310_PHY_CTRL_REG_ADDR , regnum) | FIELD_PREP(RTL9310_PHY_CTRL_MAIN_PAGE, RAW_PAGE(priv)), + .ext_page =FIELD_PREP (RTL9310_SMI_INDRT_EXT_PAGE , + RTL9310_SMI_INDRT_EXT_PAGE_NO_CHANGE), }; return otto_emdio_read_cmd(bus, RTL9310_PHY_CTRL_TYPE_C22, &cmd_data,
@@ -337,6 +341,8 @@ static int otto_emdio_9310_write_c22(struct mii_bus *bus, int port, int regnum,struct otto_emdio_cmd_regs cmd_data = { .c22_data =FIELD_PREP (RTL9310_PHY_CTRL_REG_ADDR , regnum) |FIELD_PREP (RTL9310_PHY_CTRL_MAIN_PAGE ,RAW_PAGE (priv)), + .ext_page =FIELD_PREP (RTL9310_SMI_INDRT_EXT_PAGE , +RTL9310_SMI_INDRT_EXT_PAGE_NO_CHANGE ), .io_data =FIELD_PREP (RTL9310_PHY_CTRL_INDATA , value), .port_mask_high = (u32)(BIT_ULL (port) >>32 ), .port_mask_low = (u32)(BIT_ULL (port)),
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 188/457 bpf: Fix immediate JMP JEQ/JNE on MIPS32
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Johan Almbladh <[email protected]>
[ Upstream commit db762fd96be225bd06161c9631160c755d891693 ]
An addu instruction was emitted instead of addiu, causing the immediate value 1 to be interpreted as register $at. This made the comparison result invalid when the immediate operand was negative. Note that $at is mapped to BPF_REG_AX, which is used for constant blinding.
Fix the instruction to use the immediate form.
Found with test_bpf on MIPS32r1 emulated by QEMU.
--- arch/mips/net/bpf_jit_comp32.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
arch/mips/net/bpf_jit_comp32.c +1 -1
@@ -1111,7 +1111,7 @@ static void emit_jmp_i64(struct jit_context *ctx,emit (ctx, xor, tmp,lo (dst), tmp); }if (imm <0 ) {/* Compare sign extension */ -emit (ctx, addu,MIPS_R_T9 ,hi (dst),1 ); +emit (ctx, addiu,MIPS_R_T9 ,hi (dst),1 );emit (ctx, or, tmp, tmp,MIPS_R_T9 ); }else {/* Compare zero extension */ emit (ctx, or, tmp, tmp,hi (dst));
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 189/457 bpf: Fix BSWAP 32 and 16 on MIPS64
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Johan Almbladh <[email protected]>
[ Upstream commit 8110ba09777873443db286b3cbb89b0e6311c554 ]
The 16/32-bit byteswap implementations for MIPS64r1 and earlier do not have an explicit zero extension afterwards. The input is first sign-extended to 64 bits, and the byteswap sequence can then leave the result sign-extended depending on the value of the low bits.
Add the missing zero-extension.
Found with test_bpf on MIPS64r1 emulated by QEMU.
--- arch/mips/net/bpf_jit_comp64.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
arch/mips/net/bpf_jit_comp64.c +1 -2
@@ -305,8 +305,7 @@ static void emit_bswap_r64(struct jit_context *ctx, u8 dst, u32 width)case 16 :emit_sext (ctx, dst, dst);emit_bswap_r (ctx, dst, width); -if (cpu_has_mips64r2 || cpu_has_mips64r6) -emit_zext (ctx, dst); +emit_zext (ctx, dst);break ; }clobber_reg (ctx, dst);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 190/457 tg3: use random MAC address when tg3_get_device_address fails
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ivan Delalande <[email protected]>
[ Upstream commit 4eb3f195ef08c5acaed87958297e41cc49588dde ]
Some of the tg3 NICs we use (BCM57762) reset the SRAM MAC address to the placeholder address on link flaps, tg3_chip_reset, etc. We've typically fixed it from userspace, but since e4c00ba7274b ("tg3: replace placeholder MAC address with device property") was merged, tg3 just fails probe as we don't have a way to get it through the generic device_get_mac_address infrastructure as fallback on our systems.
Make the driver assign a random address in this condition instead of being fatal for probe. Set deferred_probe_reason through dev_warn_probe if the address isn't yet available from the provider.
--- drivers/net/ethernet/broadcom/tg3.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-)
drivers/net/ethernet/broadcom/tg3.c +7 -4
@@ -17915,11 +17915,14 @@ static int tg3_init_one(struct pci_dev *pdev, err =tg3_get_device_address (tp, addr);if (err) { -dev_err (&pdev->dev, -"Could not obtain valid ethernet address, aborting\n" ); - goto err_out_apeunmap; +dev_warn_probe (&pdev->dev, err, +"Could not obtain a valid ethernet address\n" ); +if (err == -EPROBE_DEFER ) + goto err_out_apeunmap; +eth_hw_addr_random (dev); + }else { +eth_hw_addr_set (dev, addr); } -eth_hw_addr_set (dev, addr); intmbx =MAILBOX_INTERRUPT_0 +TG3_64BIT_REG_LOW ; rcvmbx =MAILBOX_RCVRET_CON_IDX_0 +TG3_64BIT_REG_LOW ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 191/457 net: stmmac: clear stale buf->page after recycling on skb build failure
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Lorenzo Bianconi <[email protected]>
[ Upstream commit 0a7822e34a0bfde31b194ac3da3253e5032b44cc ]
In stmmac_rx(), when napi_build_skb() fails the descriptor page is recycled back to the page pool with page_pool_recycle_direct(), but buf->page is left pointing at the recycled page, unlike every other consumption site in the function which clears the pointer after handing the page away.
With the stale pointer stmmac_rx_refill() skips the replacement allocation and programs the already-recycled page back into the RX descriptor.
Clear buf->page on the napi_build_skb() failure path to keep the buffer lifecycle consistent with the other consumption sites.
--- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 1 + 1 file changed, 1 insertion(+)
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +1 -0
@@ -5885,6 +5885,7 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue)if (!skb) {page_pool_recycle_direct (rx_q->page_pool, buf->page); + buf->page =NULL ; rx_dropped++; count++; goto drain_data;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 192/457 net: ipv6: keep room for the mac header in dst_dev_overhead()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yuya Kusakabe <[email protected]>
[ Upstream commit 87cd6b717e4069dca34e0866e57eaeb44d3b173e ]
The seg6, ioam6 and rpl lwtunnels size their skb_cow_head() request as the length they are about to push plus dst_dev_overhead(), then push the new headers and rebuild the mac header below them with skb_mac_header_rebuild(). That rebuild needs skb->mac_len of headroom, but dst_dev_overhead() leaves LL_RESERVED_SPACE() of the egress device, 16 bytes for plain Ethernet.
Where the mac header is longer than that, as it is on ingress through a VLAN device with reorder_hdr off, the rebuild runs out of room:skb_set_mac_header(skb, -skb->mac_len) computes a negative offset, stores it unchecked in the u16 skb->mac_header, and the memmove that follows writes skb->mac_len bytes about 64 KB past skb->head. Forwarding plain ping6 traffic through such a device reproduces it on all five seg6 encapsulation modes and on the rpl and ioam6 inline paths; skb->mac_header comes back as 65534 on a 704-byte head.
Return the larger of the two. The helper already returns skb->mac_len when it has no dst, so this only makes the other branch agree, and it covers every caller rather than each call site in turn.
--- include/net/dst.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
include/net/dst.h +2 -1
@@ -455,7 +455,8 @@ static inline unsigned int dst_dev_overhead(struct dst_entry *dst, struct sk_buff *skb) {if (likely (dst)) -return LL_RESERVED_SPACE (dst->dev); + return max_t(unsigned int, skb->mac_len, +LL_RESERVED_SPACE (dst->dev));return skb->mac_len; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 193/457 net: bcmgenet: fix 64-bit RTNL stats reading in ethtool on 32-bit systems
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Florian Fainelli <[email protected]>
[ Upstream commit 0e2bec77ea62895416600c90588f593516572bca ]
When bcmgenet was converted to 64-bit statistics, STAT_RTNL members were switched to point into struct rtnl_link_stats64, whose fields are 64-bit (__u64) regardless of architecture.
However, bcmgenet_get_ethtool_stats() retained a legacy check:
if (sizeof(unsigned long) != sizeof(u32) &&
s->stat_sizeof == sizeof(unsigned long))
On 32-bit systems, sizeof(unsigned long) == sizeof(u32), causing this condition to evaluate to false. As a result, 64-bit RTNL stats fields were read via *(u32 *)p. On 32-bit Big-Endian systems (such as MIPS BE), this reads the high 32 bits and returns 0 until the counter exceeds 4GB; on 32-bit Little-Endian systems (such as 32-bit ARM), the value is truncated to 32 bits.
Fix this by checking if s->stat_sizeof == sizeof(u64) so 64-bit fields are always read as 64-bit values.
--- drivers/net/ethernet/broadcom/genet/bcmgenet.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-)
drivers/net/ethernet/broadcom/genet/bcmgenet.c +2 -3
@@ -1346,9 +1346,8 @@ static void bcmgenet_get_ethtool_stats(struct net_device *dev, p = (char *)&stats64; p += s->stat_offset; -if (sizeof (unsigned long ) !=sizeof (u32) && - s->stat_sizeof ==sizeof (unsigned long )) - data[i] = *(unsigned long *)p; +if (s->stat_sizeof ==sizeof (u64)) + data[i] = *(u64 *)p;else data[i] = *(u32 *)p; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 194/457 net: bcmgenet: initialize u64 stats seq counter for all queues
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Florian Fainelli <[email protected]>
[ Upstream commit 3aeaa609fda19c09d5298c9fedaaa3b6229601b5 ]
bcmgenet_gstrings_stats statically defines ethtool statistics for queues 0 through GENET_MAX_MQ_CNT (4). However, bcmgenet_probe() only initialized the u64_stats_sync seq counter up to priv->hw_params->rx_queues and priv->hw_params->tx_queues.
Since priv->hw_params->rx_queues is 0 across all hardware versions (and priv->hw_params->tx_queues is 0 on GENET V1), rings 1..4 have uninitialized u64_stats_sync structures. When ethtool -S is run on 32-bit kernels, bcmgenet_get_ethtool_stats() reads stats from rx_rings[1..4], causing lockdep warnings due to the uninitialized sequence counters.
Initialize the sequence counters for all GENET_MAX_MQ_CNT + 1 queues.
--- drivers/net/ethernet/broadcom/genet/bcmgenet.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/net/ethernet/broadcom/genet/bcmgenet.c +2 -2
@@ -4135,10 +4135,10 @@ static int bcmgenet_probe(struct platform_device *pdev) priv->rx_rings[i].rx_max_coalesced_frames =1 ;/* Initialize u64 stats seq counter for 32bit machines */ -for (i =0 ; i <= priv->hw_params->rx_queues; i++) +for (i =0 ; i <=GENET_MAX_MQ_CNT ; i++) {u64_stats_init (&priv->rx_rings[i].stats64.syncp); -for (i =0 ; i <= priv->hw_params->tx_queues; i++)u64_stats_init (&priv->tx_rings[i].stats64.syncp); + }/* libphy will determine the link state */ netif_carrier_off (dev);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 195/457 net: bcmgenet: do not skip WoL power up on GENET V1
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Florian Fainelli <[email protected]>
[ Upstream commit cbbc1aee7776c7fa1d89e6cb963a23e58c495dca ]
bcmgenet_power_up() had an early check for bcmgenet_has_ext(priv) before dispatching by power mode. GENET V1 does not have the EXT block (unlike GENET V2+), which causes bcmgenet_power_up() to immediately return 0.
As a consequence, when waking up from GENET_POWER_WOL_MAGIC on GENET V1, bcmgenet_wol_power_up_cfg() is never invoked to disable the WoL clock, clear wake event masks, and restore normal PHY and MAC operations.
Move the bcmgenet_has_ext() checks to the GENET_POWER_PASSIVE and GENET_POWER_CABLE_SENSE cases where the EXT registers are actually accessed, allowing GENET_POWER_WOL_MAGIC cleanup to execute on all hardware versions.
--- drivers/net/ethernet/broadcom/genet/bcmgenet.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-)
drivers/net/ethernet/broadcom/genet/bcmgenet.c +8 -5
@@ -1762,13 +1762,12 @@ static int bcmgenet_power_up(struct bcmgenet_priv *priv,int ret =0 ;u32 reg; -if (!bcmgenet_has_ext (priv)) -return ret; - - reg =bcmgenet_ext_readl (priv,EXT_EXT_PWR_MGMT ); -switch (mode) {case GENET_POWER_PASSIVE : +if (!bcmgenet_has_ext (priv)) +break ; + + reg =bcmgenet_ext_readl (priv,EXT_EXT_PWR_MGMT ); reg &= ~(EXT_PWR_DOWN_DLL |EXT_PWR_DOWN_BIAS |EXT_ENERGY_DET_MASK );if (GENET_IS_V5 (priv) && !bcmgenet_has_ephy_16nm (priv)) {
@@ -1792,8 +1791,12 @@ static int bcmgenet_power_up(struct bcmgenet_priv *priv,break ;case GENET_POWER_CABLE_SENSE : +if (!bcmgenet_has_ext (priv)) +break ; +/* enable APD */ if (!GENET_IS_V5 (priv)) { + reg =bcmgenet_ext_readl (priv,EXT_EXT_PWR_MGMT ); reg |=EXT_PWR_DN_EN_LD ;bcmgenet_ext_writel (priv, reg,EXT_EXT_PWR_MGMT ); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 196/457 net: bcmgenet: validate Ethernet address in bcmgenet_set_mac_addr
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Florian Fainelli <[email protected]>
[ Upstream commit 273941c85fc2632cd3e56ddff737b9245de7697d ]
bcmgenet_set_mac_addr() did not check whether the provided MAC address is a valid Ethernet address before applying it. Userspace could configure an invalid address (such as all zeroes or a multicast address) while the interface is down.
Add a call to is_valid_ether_addr() and return -EADDRNOTAVAIL if the MAC address is not valid.
--- drivers/net/ethernet/broadcom/genet/bcmgenet.c | 3 +++ 1 file changed, 3 insertions(+)
drivers/net/ethernet/broadcom/genet/bcmgenet.c +3 -0
@@ -3635,6 +3635,9 @@ static int bcmgenet_set_mac_addr(struct net_device *dev, void *p)if (netif_running (dev))return -EBUSY ; +if (!is_valid_ether_addr (addr->sa_data)) +return -EADDRNOTAVAIL ; +eth_hw_addr_set (dev, addr->sa_data);return 0 ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 197/457 net: bcmgenet: mask DMA_TIMEOUT_MASK when reading DMA_RING0_TIMEOUT
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Florian Fainelli <[email protected]>
[ Upstream commit d64e277b955be4506931802837499b62c8f3968a ]
bcmgenet_get_coalesce() reads DMA_RING0_TIMEOUT to calculate rx_coalesce_usecs without masking out bits outside DMA_TIMEOUT_MASK (16 bits). If upper bits are non-zero or contain status/flags, the computed value of rx_coalesce_usecs returned to userspace via ethtool becomes corrupted.
Mask the register read with DMA_TIMEOUT_MASK before computing the timeout in microseconds.
--- drivers/net/ethernet/broadcom/genet/bcmgenet.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/net/ethernet/broadcom/genet/bcmgenet.c +2 -1
@@ -852,7 +852,8 @@ static int bcmgenet_get_coalesce(struct net_device *dev, ec->rx_max_coalesced_frames =bcmgenet_rdma_ring_readl (priv,0 ,DMA_MBUF_DONE_THRESH ); ec->rx_coalesce_usecs = -bcmgenet_rdma_readl (priv,DMA_RING0_TIMEOUT ) *8192 /1000 ; + (bcmgenet_rdma_readl (priv,DMA_RING0_TIMEOUT ) & +DMA_TIMEOUT_MASK ) *8192 /1000 ;for (i =0 ; i <= priv->hw_params->rx_queues; i++) { ring = &priv->rx_rings[i];
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 198/457 ip_gre: Reject enabling collect metadata through changelink
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xuanqiang Luo <[email protected]>
[ Upstream commit a3f315be9d30eeb6938d11fa17fd4b32d52f7c42 ]
ipgre_netlink_parms() can enable collect_md on an existing GRE, GRETAP or ERSPAN device. Unlike newlink, changelink does not enforce metadata tunnel uniqueness. Converting a non-metadata device can therefore replace the metadata receive entry for another device of the same type in the same netns. Deleting either device then clears the shared entry, breaking metadata receive lookup for the surviving device.
If parameter validation fails after collect_md is set, deleting the modified device can also clear an entry it never owned.
Reject enabling metadata mode in both changelink callbacks before any encapsulation or tunnel parameters are modified. Allow requests that repeat the metadata attribute on an existing metadata device.
--- net/ipv4/ip_gre.c | 12 ++++++++++++ 1 file changed, 12 insertions(+)
net/ipv4/ip_gre.c +12 -0
@@ -1460,6 +1460,12 @@ static int ipgre_changelink(struct net_device *dev, struct nlattr *tb[],if (!rtnl_dev_link_net_capable (dev, t->net))return -EPERM ; +if (data && data[IFLA_GRE_COLLECT_METADATA ] && !t->collect_md) { +NL_SET_ERR_MSG (extack, +"Enabling collect_md on an existing device is not supported" ); +return -EOPNOTSUPP ; + } + err =ipgre_newlink_encap_setup (dev, data);if (err)return err;
@@ -1492,6 +1498,12 @@ static int erspan_changelink(struct net_device *dev, struct nlattr *tb[],if (!rtnl_dev_link_net_capable (dev, t->net))return -EPERM ; +if (data && data[IFLA_GRE_COLLECT_METADATA ] && !t->collect_md) { +NL_SET_ERR_MSG (extack, +"Enabling collect_md on an existing device is not supported" ); +return -EOPNOTSUPP ; + } + err =ipgre_newlink_encap_setup (dev, data);if (err)return err;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 199/457 vxlan: use one headroom snapshot for neighbour replies
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sanghyun Park <[email protected]>
[ Upstream commit 481506a756dcd828ef42391cb08f38d8d96d38fc ]
vxlan_na_create() samples LL_RESERVED_SPACE() to size the reply skb and then samples it again to reserve headroom. A concurrent vxlan_changelink() can update needed_headroom between the two reads, creating a TOCTOU race. The second value can exceed the allocation and make the Ethernet header write out of bounds.
The race is reproducible on the unpatched kernel. It occurred when vxlan_na_create() generated a neighbour reply while vxlan_changelink() changed the link headroom. KASAN caught a four-byte write two bytes beyond a 704-byte skbuff_small_head allocation.
Snapshot the headroom once and use that value for both allocation and reservation.
--- drivers/net/vxlan/vxlan_core.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
drivers/net/vxlan/vxlan_core.c +4 -2
@@ -1958,13 +1958,15 @@ static struct sk_buff *vxlan_na_create(struct sk_buff *request,struct ipv6hdr *pip6;u8 *daddr;int na_olen =8 ;/* opt hdr + ETH_ALEN for target */ +int headroom;int ns_olen;int i, len;if (dev ==NULL || !pskb_may_pull (request, request->len))return NULL ; - len =LL_RESERVED_SPACE (dev) +sizeof (struct ipv6hdr ) + + headroom =LL_RESERVED_SPACE (dev); + len = headroom +sizeof (struct ipv6hdr ) +sizeof (*na) + na_olen + dev->needed_tailroom; reply =alloc_skb (len,GFP_ATOMIC );if (reply ==NULL )
@@ -1972,7 +1974,7 @@ static struct sk_buff *vxlan_na_create(struct sk_buff *request, reply->protocol =htons (ETH_P_IPV6 ); reply->dev = dev; -skb_reserve (reply,LL_RESERVED_SPACE (request->dev)); +skb_reserve (reply, headroom);skb_push (reply,sizeof (struct ethhdr ));skb_reset_mac_header (reply);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 200/457 net: xps: reject an out of range traffic class
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Norbert Szetei <[email protected]>
[ Upstream commit 4da3b7b8b50f3e2fde54a4c18a82a8e3f6223910 ]
Only the entries below dev->num_tc are valid in dev->tc_to_txq[], and dev->prio_tc_map[] may only name classes below it. netdev_set_num_tc() lowers dev->num_tc without touching either array.
netdev_txq_to_tc() walks all TC_MAX_QUEUE slots and netdev_get_prio_tc_map() returns the entry as it stands, so a leftover entry is handed out as a traffic class >= dev->num_tc. Taking that class from netdev_txq_to_tc(), __netif_set_xps_queue() rejects only a negative one and indexes an XPS map sized for dev->num_tc classes:
tci = j * num_tc + tc; RCU_INIT_POINTER(new_dev_maps->attr_map[tci], map);
attr_map[] holds nr_ids * num_tc entries and j runs over the ids named in the mask, so a class that is not below num_tc pushes tci past the end of the map for the last ids and the store overruns it.
Any caller that lowers num_tc leaves such entries behind, and mqprio_destroy() tears down with netdev_set_num_tc(dev, 0) rather than netdev_reset_tc(). After mqprio with 8 classes then 1, tc_to_txq[1..7] still describe txq 1..7. The splat is from an XPS write to txq 2 on a veth with 8 rx queues: attr_map[] has 8 * 1 entries, tci = j + 2, and j == 6 stores one past the end of the 88-byte map:
BUG: KASAN: slab-out-of-bounds in __netif_set_xps_queue (net/core/dev.c:2954) Write of size 8 at addr ffff88813016bc58 by task xps_oob/634 __netif_set_xps_queue (net/core/dev.c:2954) xps_rxqs_store (net/core/net-sysfs.c:1880) netdev_queue_attr_store (net/core/net-sysfs.c:1390) Allocated by task 634: __kmalloc_noprof (mm/slub.c:5439) __netif_set_xps_queue (net/core/dev.c:2937) The buggy address is located 0 bytes to the right of allocated 88-byte region [ffff88813016bc00, ffff88813016bc58)
Reject a class the map has no room for.
--- net/core/dev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/core/dev.c +1 -1
@@ -2897,7 +2897,7 @@ int __netif_set_xps_queue(struct net_device *dev, const unsigned long *mask, dev =netdev_get_tx_queue (dev, index)->sb_dev ? : dev; tc =netdev_txq_to_tc (dev, index); -if (tc <0 ) +if (tc <0 || tc >= num_tc)return -EINVAL ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 201/457 drm/imagination: clamp freelist reconstruction requests
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pengpeng Hou <[email protected]>
[ Upstream commit 45585c3aa285854face65293acc95eff73063d6d ]
The firmware reconstruction count controls accesses to the request's fixed freelist ID array and the copy into the fixed response array. Neither access currently bounds the count to those protocol arrays.
Clamp the count to the request capacity, which is shared by the response layout, and use that count consistently for reconstruction and response publication. Keep the firmware recovery exchange instead of dropping an oversized request without a response, as discussed with the firmware maintainer.
The issue was found by our static-analysis tool.
--- drivers/gpu/drm/imagination/pvr_free_list.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-)
drivers/gpu/drm/imagination/pvr_free_list.c +12 -3
@@ -8,6 +8,7 @@#include "pvr_vm.h" #include <drm/drm_gem.h> +#include <drm/drm_print.h> #include <linux/slab.h> #include <linux/xarray.h> #include <uapi/drm/pvr_drm.h>
@@ -612,13 +613,21 @@ pvr_free_list_process_reconstruct_req(struct pvr_device *pvr_dev, };struct rogue_fwif_freelists_reconstruction_data *resp = &resp_cmd.cmd_data.free_lists_reconstruction_data; +u32 count =min_t (u32, req->freelist_count, +ARRAY_SIZE (req->freelist_ids)); -for (u32 i =0 ; i < req->freelist_count; i++) +if (count != req->freelist_count) { +drm_warn_once (from_pvr_device (pvr_dev), +"Requested reconstruction of %u freelists, limiting to %u\n" , + req->freelist_count, count); + } + +for (u32 i =0 ; i < count; i++)pvr_free_list_reconstruct (pvr_dev, req->freelist_ids[i]); - resp->freelist_count = req->freelist_count; + resp->freelist_count = count;memcpy (resp->freelist_ids, req->freelist_ids, - req->freelist_count *sizeof (resp->freelist_ids[0 ])); + count *sizeof (resp->freelist_ids[0 ]));WARN_ON (pvr_kccb_send_cmd (pvr_dev, &resp_cmd,NULL )); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 202/457 bonding: crypto offload enabled, non-offload slave failover, rekey failed
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Dai <[email protected]>
[ Upstream commit 00efbbd40bd5fd92c67b7cf1aab8904fa59a96f6 ]
Create a bonding device (i.e. bond0) in active-backup mode, 2 slaves. Active slave: offload capable interface (i.e. eth1), primary interface. Backup slave: non-offload capable interface(i.e. eth2). Configure strongswan service swantl.conf child SA "hw_offload = crypto" Start strongswan service
IPSec Crytpo Offload is enabled on top of bond0. i.e.
ip xfrm state |grep offload
crypto offload parameters: dev bond0 dir out mode crypto crypto offload parameters: dev bond0 dir in mode crypto
Active slave eth1 takes adavantage of IPSec Crypto Offload capability.
If active slave eth1 is down for any reason (i.e. eth1 link down):
ip link set down dev eth1
non-offload capable interface eth2 failover to becomes active slave. The existing SAs can continue use software IPsec after failover. Traffic still keeps going properly.
However if eth1 link had not recovered yet, strongswan service does new child SA rekey, or uses swanctl command to do new child SA rekey, it will fail because active slave eth2 doesn't support crypto offload. In bond_ipsec_add_sa routine, it returns -EINVAL now, which is treated as fatal error by xfrm_dev_state_add routine in kernel xfrm.
To make the non-offload active slave survive the child SA rekey, need to make bond_ipsec_add_sa routine returns -EOPNOTSUPP instead when active slave doesn't support IPsec Crypto offload, the xfrm will gracefully fallback to create new SA using Software IPsec. Network traffic can keep going.
After offload capable interface eth1 link is up, becomes active slave, next time strongswan child SA rekey will create a new SA which enables crypto offload again.
--- drivers/net/bonding/bond_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/bonding/bond_main.c +1 -1
@@ -490,7 +490,7 @@ static int bond_ipsec_add_sa(struct net_device *bond_dev, !real_dev->xfrmdev_ops->xdo_dev_state_add || netif_is_bond_master(real_dev)) {NL_SET_ERR_MSG_MOD (extack,"Slave does not support ipsec offload" ); - err = -EINVAL ; + err = -EOPNOTSUPP ; goto out; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 203/457 macsec: initialize SecY before registering the netdevice
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Haseeb Malik <[email protected]>
[ Upstream commit c2de369c5c5b8599ca10fd5ca8d11fcd845c1331 ]
Creating a MACsec device with MAC offload over an LRO-capable lower device triggers a warning in rtmsg_ifinfo_build_skb() when IPv4 forwarding is enabled by default.
register_netdevice() invokes inetdev_init(), which disables LRO and emits a NETDEV_FEAT_CHANGE notification. This reaches macsec_fill_info() before macsec_add_dev() initializes the SecY. key_len is still zero, so macsec_fill_info() returns -EMSGSIZE and trips the WARN_ON in rtmsg_ifinfo_build_skb(), even though the skb has enough space.
Even without the warning, notifications during registration can report uninitialized SecY attributes, including the SCI. This ordering has existed since the driver was introduced.
Initialize the SecY and apply the new-link attributes before registration. Move MAC address inheritance into macsec_newlink() so the SCI can also be initialized before registration-time notifications report it. Move the per-CPU statistics and metadata destination allocation into ndo_init(), and release partial allocations on failure.
--- drivers/net/macsec.c | 84 +++++++++++++++++++++++--------------------- 1 file changed, 43 insertions(+), 41 deletions(-)
drivers/net/macsec.c +43 -41
@@ -3539,6 +3539,22 @@ static int macsec_dev_init(struct net_device *dev)if (err)return err; + err = -ENOMEM ; + macsec->stats = netdev_alloc_pcpu_stats(struct pcpu_secy_stats); +if (!macsec->stats) + goto destroy_gro_cells; + + macsec->secy.tx_sc.stats = + netdev_alloc_pcpu_stats(struct pcpu_tx_sc_stats); +if (!macsec->secy.tx_sc.stats) + goto free_secy_stats; + + macsec->secy.tx_sc.md_dst =metadata_dst_alloc (0 ,METADATA_MACSEC , +GFP_KERNEL ); +if (!macsec->secy.tx_sc.md_dst) + goto free_tx_sc_stats; + macsec->secy.tx_sc.md_dst->u.macsec_info.sci = macsec->secy.sci; +macsec_inherit_tso_max (dev); dev->hw_features = real_dev->hw_features &MACSEC_OFFLOAD_FEATURES ;
@@ -3551,8 +3567,6 @@ static int macsec_dev_init(struct net_device *dev)macsec_set_head_tail_room (dev); -if (is_zero_ether_addr (dev->dev_addr)) -eth_hw_addr_inherit (dev, real_dev);if (is_zero_ether_addr (dev->broadcast))memcpy (dev->broadcast, real_dev->broadcast, dev->addr_len);
@@ -3560,6 +3574,14 @@ static int macsec_dev_init(struct net_device *dev)netdev_hold (real_dev, &macsec->dev_tracker,GFP_KERNEL );return 0 ; + +free_tx_sc_stats: +free_percpu (macsec->secy.tx_sc.stats); +free_secy_stats: +free_percpu (macsec->stats); +destroy_gro_cells: +gro_cells_destroy (&macsec->gro_cells); +return err; }static void macsec_dev_uninit (struct net_device *dev)
@@ -4114,26 +4136,11 @@ static sci_t dev_to_sci(struct net_device *dev, __be16 port)return make_sci (dev->dev_addr, port); } -static int macsec_add_dev (struct net_device *dev,sci_t sci,u8 icv_len) +static void macsec_init_secy (struct net_device *dev,sci_t sci,u8 icv_len) {struct macsec_dev *macsec =macsec_priv (dev);struct macsec_secy *secy = &macsec->secy; - macsec->stats = netdev_alloc_pcpu_stats(struct pcpu_secy_stats); -if (!macsec->stats) -return -ENOMEM ; - - secy->tx_sc.stats = netdev_alloc_pcpu_stats(struct pcpu_tx_sc_stats); -if (!secy->tx_sc.stats) -return -ENOMEM ; - - secy->tx_sc.md_dst =metadata_dst_alloc (0 ,METADATA_MACSEC ,GFP_KERNEL ); -if (!secy->tx_sc.md_dst) -/* macsec and secy percpu stats will be freed when unregistering -* net_device in macsec_free_netdev() -*/ -return -ENOMEM ; -if (sci ==MACSEC_UNDEF_SCI ) sci =dev_to_sci (dev,MACSEC_PORT_ES );
@@ -4147,15 +4154,12 @@ static int macsec_add_dev(struct net_device *dev, sci_t sci, u8 icv_len) secy->xpn =DEFAULT_XPN ; secy->sci = sci; - secy->tx_sc.md_dst->u.macsec_info.sci = sci; secy->tx_sc.active = true; secy->tx_sc.encoding_sa =DEFAULT_ENCODING_SA ; secy->tx_sc.encrypt =DEFAULT_ENCRYPT ; secy->tx_sc.send_sci =DEFAULT_SEND_SCI ; secy->tx_sc.end_station = false; secy->tx_sc.scb = false; - -return 0 ; }static struct lock_class_key macsec_netdev_addr_lock_key;
@@ -4218,6 +4222,24 @@ static int macsec_newlink(struct net_device *dev,if (rx_handler && rx_handler != macsec_handle_frame)return -EBUSY ; +if (is_zero_ether_addr (dev->dev_addr)) +eth_hw_addr_inherit (dev, real_dev); + +if (data && data[IFLA_MACSEC_SCI ]) + sci =nla_get_sci (data[IFLA_MACSEC_SCI ]); +else if (data && data[IFLA_MACSEC_PORT ]) + sci =dev_to_sci (dev,nla_get_be16 (data[IFLA_MACSEC_PORT ])); +else + sci =dev_to_sci (dev,MACSEC_PORT_ES ); + +/* Registration can notify listeners before returning. */ +macsec_init_secy (dev, sci, icv_len); +if (data) { + err =macsec_changelink_common (dev, data); +if (err) +return err; + } + err =register_netdevice (dev);if (err <0 )return err;
@@ -4230,31 +4252,11 @@ static int macsec_newlink(struct net_device *dev,if (err <0 ) goto unregister; -/* need to be already registered so that ->init has run and -* the MAC addr is set -*/ -if (data && data[IFLA_MACSEC_SCI ]) - sci =nla_get_sci (data[IFLA_MACSEC_SCI ]); -else if (data && data[IFLA_MACSEC_PORT ]) - sci =dev_to_sci (dev,nla_get_be16 (data[IFLA_MACSEC_PORT ])); -else - sci =dev_to_sci (dev,MACSEC_PORT_ES ); -if (rx_handler &&sci_exists (real_dev, sci)) { err = -EBUSY ; goto unlink; } - err =macsec_add_dev (dev, sci, icv_len); -if (err) - goto unlink; - -if (data) { - err =macsec_changelink_common (dev, data); -if (err) - goto del_dev; - } -/* If h/w offloading is available, propagate to the device */ if (macsec_is_offloaded (macsec)) {const struct macsec_ops *ops;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 204/457 net/sched: act_ife: validate metadata length before decoding
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Fang Xieyan <[email protected]>
[ Upstream commit d6ec384c87cc851cfd13bb18c99ce351ccee6192 ]
skbmark_decode(), skbprio_decode() and skbtcindex_decode() read fixed-size values from the TLV payload without validating its length.
A malformed IFE frame can declare a shorter payload, causing the decoders to consume bytes beyond the declared metadata value:
[TLV type=IFE_META_SKBMARK len=4]
-> dlen == 0, but decode reads 4 bytes
The decoder may therefore set skb metadata from unintended input.
Validate the payload length before decoding and return -EINVAL for invalid lengths. Read the values with get_unaligned_be32() and get_unaligned_be16(), as TLV payloads are not guaranteed to be aligned. Teach tcf_ife_decode() to log a decoder error separately from an unknown metaid; both are counted as overlimits and decoding continues with the remaining metadata.
The metadata length issue was found by an automated audit of the IFE decode path at v6.18-rc7 and reproduced with a userspace sanitizer model of the decode path. Compile-tested on x86_64 with defconfig and NET_ACT_IFE=y: act_ife.o and the three act_meta_*.o build warning-free.
--- net/sched/act_ife.c | 17 ++++++++++++----- net/sched/act_meta_mark.c | 6 ++++-- net/sched/act_meta_skbprio.c | 6 ++++-- net/sched/act_meta_skbtcindex.c | 6 ++++-- 4 files changed, 24 insertions(+), 11 deletions(-)
net/sched/act_ife.c +12 -5
@@ -737,6 +737,7 @@ static int tcf_ife_decode(struct sk_buff *skb, const struct tc_action *a,u8 *curr_data;u16 mtype;u16 dlen; +int ret; curr_data =ife_tlv_meta_decode (tlv_data, ifehdr_end, &mtype, &dlen,NULL );
@@ -745,13 +746,19 @@ static int tcf_ife_decode(struct sk_buff *skb, const struct tc_action *a,return TC_ACT_SHOT ; } -if (find_decode_metaid (skb, p, mtype, dlen, curr_data)) { -/* abuse overlimits to count when we receive metadata -* but dont have an ops for it + ret =find_decode_metaid (skb, p, mtype, dlen, curr_data); +if (ret <0 ) { +/* abuse overlimits to count metadata we cannot +* decode: no ops for it, or the decoder rejected it */ -pr_info_ratelimited ("Unknown metaid %d dlen %d\n" , - mtype, dlen);qstats_cpu_overlimit_inc (ife->common.cpu_qstats); + +if (ret == -ENOENT ) +pr_info_ratelimited ("Unknown metaid %d dlen %d\n" , + mtype, dlen); +else +pr_info_ratelimited ("Failed to decode metaid %d dlen %d err %d\n" , + mtype, dlen, ret); } }
net/sched/act_meta_mark.c +4 -2
@@ -10,6 +10,7 @@#include <linux/string.h> #include <linux/errno.h> #include <linux/skbuff.h> +#include <linux/unaligned.h> #include <linux/rtnetlink.h> #include <linux/module.h> #include <linux/init.h>
@@ -28,9 +29,10 @@ static int skbmark_encode(struct sk_buff *skb, void *skbdata,static int skbmark_decode (struct sk_buff *skb,void *data,u16 len) { -u32 ifemark = *(u32 *)data; +if (len !=sizeof (u32)) +return -EINVAL ; - skb->mark =ntohl (ifemark); + skb->mark =get_unaligned_be32 (data);return 0 ; }
net/sched/act_meta_skbprio.c +4 -2
@@ -10,6 +10,7 @@#include <linux/string.h> #include <linux/errno.h> #include <linux/skbuff.h> +#include <linux/unaligned.h> #include <linux/rtnetlink.h> #include <linux/module.h> #include <linux/init.h>
@@ -33,9 +34,10 @@ static int skbprio_encode(struct sk_buff *skb, void *skbdata,static int skbprio_decode (struct sk_buff *skb,void *data,u16 len) { -u32 ifeprio = *(u32 *)data; +if (len !=sizeof (u32)) +return -EINVAL ; - skb->priority =ntohl (ifeprio); + skb->priority =get_unaligned_be32 (data);return 0 ; }
net/sched/act_meta_skbtcindex.c +4 -2
@@ -10,6 +10,7 @@#include <linux/string.h> #include <linux/errno.h> #include <linux/skbuff.h> +#include <linux/unaligned.h> #include <linux/rtnetlink.h> #include <linux/module.h> #include <linux/init.h>
@@ -28,9 +29,10 @@ static int skbtcindex_encode(struct sk_buff *skb, void *skbdata,static int skbtcindex_decode (struct sk_buff *skb,void *data,u16 len) { -u16 ifetc_index = *(u16 *)data; +if (len !=sizeof (u16)) +return -EINVAL ; - skb->tc_index =ntohs (ifetc_index); + skb->tc_index =get_unaligned_be16 (data);return 0 ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 205/457 net: emac: move setting of netops to fix crash
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christian Lamparter <[email protected]>
[ Upstream commit 7c9f391ec89cb621d7af375ace2eb9a6248e5b9d ]
fixes the following crash on driver initialization:
|BUG: Kernel NULL pointer dereference on read at 0x00000158 |Faulting instruction address: 0xc0566b40 |Oops: Kernel access of bad area, sig: 11 [#1] |BE PAGE_SIZE=4K PowerPC 44x Platform |Modules linked in: |CPU: 0 UID: 0 PID: 1 Comm: swapper/0 Tainted: GW 7.3.0-rc3+ #1 |Tainted: [W]=WARN |Hardware name: MyBook Live APM821XX 0x12c41c83 PowerPC 44x Platform |NIP: c0566b40 LR: c05648f8 CTR: c04c1e9c |REGS: c1053a20 TRAP: 0300 Tainted: GW (7.3.0-rc3+) |MSR: 0002b000 <CE,EE,FP,ME> CR: 24008808 XER: 00000000 |DEAR: 00000158 ESR: 00000000 |GPR00: c05648f8 c1053b10 c1063600 c1030000 c5ab3000 00000000 [...] |GPR08: 00000002 00000000 00000000 c1053b40 84002808 00000000 [...] |GPR16: cfffd210 00000002 c0beafcc cfffc960 00000000 c1030644 [...] |GPR24: c0beafbc c1037000 00000000 0000000a 00000000 c1030000 [...] |NIP [c0566b40] phy_link_topo_add_phy+0x2c/0x1d0 |LR [c05648f8] phy_attach_direct+0x1a4/0x368 |Call Trace: |[c1053b10] [c0811e04] klist_put+0x54/0xb4 (unreliable) |[c1053b40] [c05648f8] phy_attach_direct+0x1a4/0x368 |[c1053b70] [c0564ae8] phy_connect_direct+0x2c/0x60 |[c1053b90] [c056c7a8] of_phy_connect+0x50/0x74 |[c1053bc0] [c0572a88] emac_probe+0xd50/0x119c |[c1053c90] [c04cb770] platform_probe+0x74/0xa4 |[c1053cb0] [c04c8f08] really_probe+0x120/0x2b0 |[c1053cd0] [c04c9254] __driver_probe_device+0x1bc/0x1fc |[c1053d00] [c04c9334] driver_probe_device+0x38/0xa8 |[c1053d30] [c04c9560] __driver_attach+0xf4/0x10c |[c1053d50] [c04c6af8] bus_for_each_dev+0x68/0xd0 |[c1053d90] [c04c7c34] bus_add_driver+0xcc/0x1ec |[c1053dc0] [c04c9f6c] driver_register+0xcc/0x110 |[c1053de0] [c0aa3f40] emac_init+0x1c4/0x200
This bug showed up starting with v7.3-rc1. At the NIP in phy_link_topo_add_phy() is a netdev_need_ops_lock() check.
This was added by the following
commit ded86da4bbb7 ("net: ethtool: relax ethnl_req_get_phydev() locking assertion")
The bug shows up because at the time of_phy_connect() was called, the netdev_ops were *not yet* determined. My fix is to move the code that sets netdev_ops+commac.ops+ethtool_ops further up as emac_init_config() derives that by looking at the device-tree and sets the required dev->phy_mode accordingly.
During review, the Sashiko bot's AI stated that the commac assignment became a dead store. Great catch! To keep the original behavior as-is, one mentioned option "set dev->commac.ops = &emac_commac_ops only in the non-gige case?" sounded like a great plan. So the dev->commac.ops assignment for the non-gige-case moves into the else block.
This patch was tested on a WD MyBook Live (RGMII). the device now works again.
--- drivers/net/ethernet/ibm/emac/core.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-)
drivers/net/ethernet/ibm/emac/core.c +9 -7
@@ -3044,6 +3044,15 @@ static int emac_probe(struct platform_device *ofdev)if (err) goto err_gone; +if (emac_phy_supports_gige (dev->phy_mode)) { + ndev->netdev_ops = &emac_gige_netdev_ops; + dev->commac.ops = &emac_commac_sg_ops; + }else { + ndev->netdev_ops = &emac_netdev_ops; + dev->commac.ops = &emac_commac_ops; + } + ndev->ethtool_ops = &emac_ethtool_ops; + dev->emacp =devm_platform_ioremap_resource (ofdev,0 );if (IS_ERR (dev->emacp)) { err =PTR_ERR (dev->emacp);
@@ -3076,7 +3085,6 @@ static int emac_probe(struct platform_device *ofdev) dev->mdio_instance =platform_get_drvdata (dev->mdio_dev);/* Register with MAL */ - dev->commac.ops = &emac_commac_ops; dev->commac.dev = dev; dev->commac.tx_chan_mask =MAL_CHAN_MASK (dev->mal_tx_chan); dev->commac.rx_chan_mask =MAL_CHAN_MASK (dev->mal_rx_chan);
@@ -3144,12 +3152,6 @@ static int emac_probe(struct platform_device *ofdev) ndev->features |= ndev->hw_features |NETIF_F_RXCSUM ; } ndev->watchdog_timeo =5 *HZ ; -if (emac_phy_supports_gige (dev->phy_mode)) { - ndev->netdev_ops = &emac_gige_netdev_ops; - dev->commac.ops = &emac_commac_sg_ops; - }else - ndev->netdev_ops = &emac_netdev_ops; - ndev->ethtool_ops = &emac_ethtool_ops;/* MTU range: 46 - 1500 or whatever is in OF */ ndev->min_mtu =EMAC_MIN_MTU ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 206/457 bpf: Zero-fill other CPUs when BPF_F_CPU creates a per-cpu hash element
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Donggeun Yoo <[email protected]>
[ Upstream commit c3a66e5f5bab3912e9f84223c5a982bf4333d5a1 ]
pcpu_init_value() initializes the per-cpu area of a newly created [lru_]percpu_hash element. The area is recycled, so when the value comes from a BPF program (onallcpus == false) it writes the running CPU's slot and zeroes the rest.
bpf_percpu_hash_update() passes onallcpus == true, which delegates to pcpu_copy_value(). pcpu_copy_value() writes only the CPU named in map_flags when BPF_F_CPU is set, so on the create path the other slots keep the recycled element's values:
update(k1, 0xdeadc0de, BPF_F_ALL_CPUS) every CPU holds 0xdeadc0de delete(k1) element back on the freelist update(k2, 0xc0ffee, BPF_F_CPU | 0) creates, writes CPU 0 only lookup(k2) CPU 0 0xc0ffee, rest 0xdeadc0de
Zero-fill the other CPUs on that arm too.
--- kernel/bpf/hashtab.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-)
kernel/bpf/hashtab.c +7 -4
@@ -1055,14 +1055,17 @@ static void pcpu_init_value(struct bpf_htab *htab, void __percpu *pptr,/* When not setting the initial value on all cpus, zero-fill element * values for other cpus. Otherwise, bpf program has no way to ensure * known initial values for cpus other than current one -* (onallcpus=false always when coming from bpf prog). +* (onallcpus=false always when coming from bpf prog, +* map_flags & BPF_F_CPU when coming from syscall but setting +* only one cpu). */ -if (!onallcpus) { -int current_cpu =raw_smp_processor_id (); +if (!onallcpus || (map_flags &BPF_F_CPU )) { +int init_cpu = (map_flags &BPF_F_CPU ) ? map_flags >>32 : +raw_smp_processor_id ();int cpu;for_each_possible_cpu (cpu) { -if (cpu == current_cpu) +if (cpu == init_cpu)copy_map_value (&htab->map,per_cpu_ptr (pptr, cpu), value);else /* Since elem is preallocated, we cannot touch special fields */ zero_map_value (&htab->map,per_cpu_ptr (pptr, cpu));
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 207/457 net: dsa: mt7530: fix NULL dereference on unbind of MT7531 and MT7621
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Aleksei Sviridkin <[email protected]>
[ Upstream commit c2cdef41e0b4d8ed23a5b41e6ad4e64594e055e4 ]
The core and io supplies are only requested for ID_MT7530: both the devm_regulator_get() in probe and the regulator_enable() in mt7530_setup() are guarded by the switch id, but mt7530_remove() disables them unconditionally. On an MT7621 or an MT7531 both pointers are still NULL from devm_kzalloc(), so rmmod or a sysfs unbind calls regulator_disable() on NULL.
--- drivers/net/dsa/mt7530-mdio.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-)
drivers/net/dsa/mt7530-mdio.c +11 -9
@@ -227,15 +227,17 @@ mt7530_remove(struct mdio_device *mdiodev)if (!priv)return ; - ret =regulator_disable (priv->core_pwr); -if (ret <0 ) -dev_err (priv->dev, -"Failed to disable core power: %d\n" , ret); - - ret =regulator_disable (priv->io_pwr); -if (ret <0 ) -dev_err (priv->dev,"Failed to disable io pwr: %d\n" , - ret); +if (priv->id ==ID_MT7530 ) { + ret =regulator_disable (priv->core_pwr); +if (ret <0 ) +dev_err (priv->dev, +"Failed to disable core power: %d\n" , ret); + + ret =regulator_disable (priv->io_pwr); +if (ret <0 ) +dev_err (priv->dev,"Failed to disable io pwr: %d\n" , + ret); + }mt7530_remove_common (priv);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 208/457 net: dsa: mt7530: leave the MDIO IRQ mappings to regmap-irq
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Aleksei Sviridkin <[email protected]>
[ Upstream commit 0d80ba0a204c6a16bd7778b50de578dff107c0fe ]
mt7530_remove_common() disposes the per-PHY interrupt mappings from .remove, but the regmap-irq chip that owns the domain is devm-registered, so its parent interrupt is only freed once .remove has returned. The switch's own regmap-irq thread can therefore still dispatch on a mapping that is already gone: irq_find_mapping() returns 0, irq_to_desc() returns NULL and handle_nested_irq() locks desc->lock without checking it. The attached PHYs have not given those interrupts back yet either, which the kernel warns about a moment before the fault.
regmap_del_irq_chip() disposes the same mappings itself, after freeing the parent interrupt and before removing the domain, so there is nothing left for the driver to do here. Until it runs the descriptors stay alive, and a late dispatch on one of them is harmless: dsa_unregister_switch() has freed the PHY handlers by then, so handle_nested_irq() finds no action and returns.
--- drivers/net/dsa/mt7530.c | 3 --- 1 file changed, 3 deletions(-)
drivers/net/dsa/mt7530.c +0 -3
@@ -3539,9 +3539,6 @@ EXPORT_SYMBOL_GPL(mt7530_probe_common);void mt7530_remove_common (struct mt7530_priv *priv) { -if (priv->irq_domain) -mt7530_free_mdio_irq (priv); -dsa_unregister_switch (priv->ds);mutex_destroy (&priv->reg_mutex);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 209/457 tcp: fix use-after-free of retransmit_skb_hint in tcp_send_synack()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yilin Zhang <[email protected]>
[ Upstream commit fe99bbeee5c5dbd3abc30721a8079ced59649d97 ]
When tcp_send_synack() replaces the cloned SYN skb at the head of the retransmit queue with a copy, it frees the original with tcp_rtx_queue_unlink_and_free() and only repairs tp->highest_sack. tp->retransmit_skb_hint keeps pointing at the freed skbuff_fclone_cache object.
The dangling hint is read in tcp_verify_retransmit_hint() and used as the root of the rbtree walk in tcp_xmit_retransmit_queue(). An unprivileged TFO client (sendmsg(MSG_FASTOPEN)) can arm the hint with an attacker-supplied ICMP fragmentation-needed message, after which a simultaneous open frees the armed SYN skb:
BUG: KASAN: slab-use-after-free in tcp_mark_skb_lost (net/ipv4/tcp_input.c:1316) Read of size 4 at addr ffff88800604d928 by task swapper/1/0 Call Trace: tcp_mark_skb_lost (net/ipv4/tcp_input.c:1316) tcp_simple_retransmit (net/ipv4/tcp_input.c:3158) tcp_v4_err (net/ipv4/tcp_ipv4.c:587)
Sync the hint to the copy.
--- net/ipv4/tcp_output.c | 3 +++ 1 file changed, 3 insertions(+)
net/ipv4/tcp_output.c +3 -0
@@ -3886,6 +3886,7 @@ void tcp_send_active_reset(struct sock *sk, enum sk_rst_reason reason)*/ int tcp_send_synack (struct sock *sk) { +struct tcp_sock *tp =tcp_sk (sk);struct sk_buff *skb; skb =tcp_rtx_queue_head (sk);
@@ -3903,6 +3904,8 @@ int tcp_send_synack(struct sock *sk)if (!nskb)return -ENOMEM ;INIT_LIST_HEAD (&nskb->tcp_tsorted_anchor); +if (skb == tp->retransmit_skb_hint) + tp->retransmit_skb_hint = nskb;tcp_highest_sack_replace (sk, skb, nskb);tcp_rtx_queue_unlink_and_free (skb, sk);__skb_header_release (nskb);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 210/457 net: phylink: record the PHY only once bringup cannot fail
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Aleksei Sviridkin <[email protected]>
[ Upstream commit a940003f44e7e441c228151dd212642152700ec8 ]
phylink_bringup_phy() stores the PHY in pl->phydev before its last fallible step: on a MAC whose phylink ops implement LPI, phy_eee_rx_clock_stop() can fail with a real MDIO error. The callers unwind with phy_detach(), which knows nothing about pl->phydev, so a pointer to a PHY that is no longer attached outlives the failed connect.
What that costs depends on how the caller got here. phylink_connect_phy() goes through phylink_attach_phy(), which refuses to attach while pl->phydev is set, turning a transient MDIO error into a permanent -EBUSY. The SFP path is worse than that: sfp_sm_probe_phy() answers the failure with phy_device_remove() and phy_device_free(), and it assigns sfp->mod_phy only past that error return, so nothing clears pl->phydev and it is left pointing at a freed phy_device that phylink_resolve() and the ethtool helpers go on reading. phylink_fwnode_phy_connect() has no such check, so a later connect overwrites the stale pointer and hides the problem. A disconnect does
not: phylink_disconnect_phy() hands that pointer to phy_disconnect(), and the second phy_detach() on the same PHY drops references the first one already released.
Found while making a DSA port survive a PHY whose driver arrives after the switch probes: keeping the port across a failed connect and retrying is what makes this window reachable.
Publish the pointer after the last call that can fail instead of unwinding it afterwards. Nothing between the two points reads pl->phydev, and the registration that follows cannot fail:phy_request_interrupt() falls back to polling on its own. The PHY-side state keeps the order it had, so no MDIO operation moves relative to another.
--- drivers/net/phy/phylink.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-)
drivers/net/phy/phylink.c +17 -3
@@ -2131,7 +2131,6 @@ static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy,mutex_lock (&pl->phydev_mutex);mutex_lock (&phy->lock);mutex_lock (&pl->state_mutex); - pl->phydev = phy; pl->phy_state.interface = interface; pl->phy_state.pause =MLO_PAUSE_NONE ; pl->phy_state.speed =SPEED_UNKNOWN ;
@@ -2198,10 +2197,25 @@ static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy, ret =0 ; } -if (ret ==0 &&phy_interrupt_is_valid (phy)) +if (ret) +return ret; + +/* Nothing below can fail, so the PHY can be recorded now. Doing it +* here rather than above keeps a failed bringup from leaving +* pl->phydev pointing at a PHY the caller is about to detach. +*/ +mutex_lock (&pl->phydev_mutex); +mutex_lock (&phy->lock); +mutex_lock (&pl->state_mutex); + pl->phydev = phy; +mutex_unlock (&pl->state_mutex); +mutex_unlock (&phy->lock); +mutex_unlock (&pl->phydev_mutex); + +if (phy_interrupt_is_valid (phy))phy_request_interrupt (phy); -return ret; +return 0 ; } static int phylink_attach_phy(struct phylink *pl, struct phy_device *phy,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 211/457 net: wangxun: implement soft quiesce for PCIe error recovery
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jiawen Wu <[email protected]>
[ Upstream commit c023e9769de94cb7b7897297e71f50b0f436c473 ]
Function wx_soft_quiesce() provide a lightweight shutdown path during PCIe error recovery. It avoids MMIO-dependent operations in PCIe error status.
Waiting for the service task to complete may unnecessarily delay PCIe error recovery, especially if the work item is already blocked by the hardware failure that triggered AER. So the service task is not explicitly cancelled in quiesce path. As a measure to block the service task, the checking of WX_STATE_DOWN and WX_STATE_RESETTING is added at the entry of relevant work item.
Stable-dep-of: 3173cba11701 ("net: libwx: fix races in Tx timestamp handling")
--- drivers/net/ethernet/wangxun/libwx/wx_lib.c | 18 +++++++++++++ drivers/net/ethernet/wangxun/libwx/wx_lib.h | 1 + drivers/net/ethernet/wangxun/libwx/wx_ptp.c | 27 +++++++++++++++++++ drivers/net/ethernet/wangxun/libwx/wx_ptp.h | 1 + .../net/ethernet/wangxun/txgbe/txgbe_main.c | 16 +++++++++++ 5 files changed, 63 insertions(+)
drivers/net/ethernet/wangxun/libwx/wx_lib.c +18 -0
@@ -3348,5 +3348,23 @@ void wx_service_timer(struct timer_list *t) }EXPORT_SYMBOL (wx_service_timer); +void wx_soft_quiesce (struct wx *wx) +{ +if (!netif_running (wx->netdev) || +test_and_set_bit (WX_STATE_DOWN , wx->state)) +return ; + +pci_clear_master (wx->pdev); +netif_tx_stop_all_queues (wx->netdev); +netif_carrier_off (wx->netdev); +netif_tx_disable (wx->netdev); +wx_napi_disable_all (wx); +wx_ptp_quiesce (wx); + +clear_bit (WX_FLAG_NEED_DO_RESET , wx->flags); +timer_delete_sync (&wx->service_timer); +} +EXPORT_SYMBOL (wx_soft_quiesce); +MODULE_DESCRIPTION ("Common library for Wangxun(R) Ethernet drivers." );MODULE_LICENSE ("GPL" );
drivers/net/ethernet/wangxun/libwx/wx_lib.h +1 -0
@@ -41,5 +41,6 @@ void wx_set_ring(struct wx *wx, u32 new_tx_count,void wx_service_event_schedule (struct wx *wx);void wx_service_event_complete (struct wx *wx);void wx_service_timer (struct timer_list *t); +void wx_soft_quiesce (struct wx *wx);#endif /* _WX_LIB_H_ */
drivers/net/ethernet/wangxun/libwx/wx_ptp.c +27 -0
@@ -321,6 +321,9 @@ static long wx_ptp_do_aux_work(struct ptp_clock_info *ptp) struct wx *wx = container_of(ptp, struct wx, ptp_caps);int ts_done; +if (!test_bit (WX_STATE_PTP_RUNNING , wx->state)) +return HZ ; + ts_done =wx_ptp_tx_hwtstamp_work (wx);wx_ptp_overflow_check (wx);
@@ -836,6 +839,30 @@ void wx_ptp_stop(struct wx *wx) }EXPORT_SYMBOL (wx_ptp_stop); +void wx_ptp_quiesce (struct wx *wx) +{ +if (!test_and_clear_bit (WX_STATE_PTP_RUNNING , wx->state)) +return ; + +clear_bit (WX_FLAG_PTP_PPS_ENABLED , wx->flags); + +if (wx->ptp_clock) +ptp_cancel_worker_sync (wx->ptp_clock); + +if (wx->ptp_tx_skb) { +dev_kfree_skb_any (wx->ptp_tx_skb); + wx->ptp_tx_skb =NULL ; + } +clear_bit_unlock (WX_STATE_PTP_TX_IN_PROGRESS , wx->state); + +if (wx->ptp_clock) { +ptp_clock_unregister (wx->ptp_clock); + wx->ptp_clock =NULL ; +dev_info (&wx->pdev->dev,"removed PHC on %s\n" , wx->netdev->name); + } +} +EXPORT_SYMBOL (wx_ptp_quiesce); +/** * wx_ptp_rx_hwtstamp - utility function which checks for RX time stamp * @wx: pointer to wx struct
drivers/net/ethernet/wangxun/libwx/wx_ptp.h +1 -0
@@ -10,6 +10,7 @@ void wx_ptp_reset(struct wx *wx);void wx_ptp_init (struct wx *wx);void wx_ptp_suspend (struct wx *wx);void wx_ptp_stop (struct wx *wx); +void wx_ptp_quiesce (struct wx *wx);void wx_ptp_rx_hwtstamp (struct wx *wx,struct sk_buff *skb);int wx_hwtstamp_get (struct net_device *dev,struct kernel_hwtstamp_config *cfg);
drivers/net/ethernet/wangxun/txgbe/txgbe_main.c +16 -0
@@ -93,12 +93,24 @@ static void txgbe_module_detection_subtask(struct wx *wx) {int err; +if (test_bit (WX_STATE_DOWN , wx->state) || +test_bit (WX_STATE_RESETTING , wx->state)) +return ; +if (!test_and_clear_bit (WX_FLAG_NEED_MODULE_RESET , wx->flags))return ;/* wait for SFF module ready */ msleep (200 ); +/* Re-check state to avoid racing with down/reset paths. +* Module identification is deferred to the next up event, +* so it is safe to bail out here. +*/ +if (test_bit (WX_STATE_DOWN , wx->state) || +test_bit (WX_STATE_RESETTING , wx->state)) +return ; + err =txgbe_identify_module (wx);if (err == -ENODEV )set_bit (WX_FLAG_NEED_MODULE_RESET , wx->flags);
@@ -106,6 +118,10 @@ static void txgbe_module_detection_subtask(struct wx *wx)static void txgbe_link_config_subtask (struct wx *wx) { +if (test_bit (WX_STATE_DOWN , wx->state) || +test_bit (WX_STATE_RESETTING , wx->state)) +return ; +if (!test_and_clear_bit (WX_FLAG_NEED_LINK_CONFIG , wx->flags))return ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 212/457 net: libwx: fix races in Tx timestamp handling
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jiawen Wu <[email protected]>
[ Upstream commit 3173cba1170131816972ed2b6185cb970ba8b747 ]
wx->ptp_tx_skb is shared between the Tx path, the PTP auxiliary worker and the timestamp cleanup paths. The WX_STATE_PTP_TX_IN_PROGRESS bit prevents multiple Tx paths from submitting timestamp requests, but does not serialize the worker against cleanup.
As a result, wx_ptp_clear_tx_timestamp() can free an skb after wx_ptp_tx_hwtstamp_work() has obtained its pointer. The worker may then pass the freed skb to skb_tstamp_tx() and release the same reference again.
The cleanup path may also clear the in-progress bit while the worker is still processing the old skb. This allows the Tx path to publish a new skb which the worker can subsequently overwrite with NULL, leaking its reference.
Add a dedicated spinlock to protect publication and consumption of the Tx timestamp skb. Detach the skb and clear the in-progress bit while holding the lock, then deliver the timestamp and release the skb after dropping it. Use the same locked cleanup in the quiesce path, but keep the detach there free of register accesses: quiesce runs during PCIe error recovery, where MMIO is not reliable, and it deliberately did not touch the device before. The lock is taken with interrupts disabled, because netpoll can call ndo_start_xmit() with hard interrupts already off.
When handling a Tx DMA mapping failure, keep the transmit path reference until after comparing the skb under the lock. This prevents skb address reuse from making the error path mistake a newer timestamp request for the failed one.
--- drivers/net/ethernet/wangxun/libwx/wx_hw.c | 1 + drivers/net/ethernet/wangxun/libwx/wx_lib.c | 47 ++++-- drivers/net/ethernet/wangxun/libwx/wx_ptp.c | 142 +++++++++++++------ drivers/net/ethernet/wangxun/libwx/wx_type.h | 2 + 4 files changed, 132 insertions(+), 60 deletions(-)
drivers/net/ethernet/wangxun/libwx/wx_hw.c +1 -0
@@ -2517,6 +2517,7 @@ int wx_sw_init(struct wx *wx) }spin_lock_init (&wx->hw_stats_lock); +spin_lock_init (&wx->ptp_tx_lock);mutex_init (&wx->reset_lock);bitmap_zero (wx->state,WX_STATE_NBITS );bitmap_zero (wx->flags,WX_PF_FLAGS_NBITS );
drivers/net/ethernet/wangxun/libwx/wx_lib.c +34 -13
77 lines not on this page · raw message
drivers/net/ethernet/wangxun/libwx/wx_ptp.c +95 -47
203 lines not on this page · raw message
drivers/net/ethernet/wangxun/libwx/wx_type.h +2 -0
@@ -1413,6 +1413,8 @@ struct wx {unsigned long last_overflow_check;unsigned long last_rx_ptp_check;unsigned long ptp_tx_start; +/* protects ptp_tx_skb, ptp_tx_start and the in-progress state bit */ +spinlock_t ptp_tx_lock;seqlock_t hw_tc_lock;/* seqlock for ptp */ struct cyclecounter hw_cc;struct timecounter hw_tc;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 213/457 nfp: hold IPsec RX state under the XArray lock
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sang-Hoon Choi <[email protected]>
[ Upstream commit 1a983a4e14c635c40354be110cd9a1a5c94e01e6 ]
nfp_net_ipsec_rx() drops the XArray lock before taking a reference to the xfrm_state it found. The delete path can erase the entry and drop the last state reference in that interval. RX can then try to increment a zero refcount after the state has been queued for destruction.
The driver queues firmware invalidation asynchronously; the delete path does not wait for the command to complete or drain pending RX processing.
The XFRM garbage collector waits for an RCU grace period before freeing the state. That delays reclamation but does not make acquiring a reference from zero valid.
Take the xfrm_state reference before releasing the XArray lock so xa_erase() cannot run between lookup and reference acquisition.
--- drivers/net/ethernet/netronome/nfp/crypto/ipsec.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/net/ethernet/netronome/nfp/crypto/ipsec.c +2 -1
@@ -625,11 +625,12 @@ int nfp_net_ipsec_rx(struct nfp_meta_parsed *meta, struct sk_buff *skb)xa_lock (&nn->xa_ipsec); x =xa_load (&nn->xa_ipsec, saidx); +if (x) +xfrm_state_hold (x);xa_unlock (&nn->xa_ipsec);if (!x)return -EINVAL ; -xfrm_state_hold (x); sp->xvec[sp->len++] = x; sp->olen++; xo =xfrm_offload (skb);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 214/457 net/rds: size a connections path set by the transport it ends up with
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Allison Henderson <[email protected]>
[ Upstream commit ba3d1f480c7a3fba963e7867ad6cc557c197acbc ]
__rds_conn_create() computes npaths from the caller's transport before it decides whether a connection to one of the host's own addresses is to be handled by the loopback transport instead. That substitution is what an RDS/TCP socket sending to a local address gets, and after it the path init loop still runs for the TCP transport's RDS_MPATH_WORKERS paths and allocates an ordered workqueue for each, while rds_loop_conn_alloc() only ever provides transport data for path 0.
rds_conn_destroy() sizes its teardown from c_trans, by then the loopback transport, so it visits path 0 only - and rds_conn_path_destroy() would skip the other paths anyway, since it returns before destroy_workqueue() for a path without transport data. kfree(c_path) then drops the last pointers to seven workqueues. That repeats for every such connection, on every netns teardown or module unload, and every distinct local destination address is a separate connection.
Recompute npaths once the transport is final, so that creation and destruction agree on the set of paths. The c_path array stays sized for the caller's transport; the unused entries are freed with it.
--- net/rds/connection.c | 6 ++++++ 1 file changed, 6 insertions(+)
net/rds/connection.c +6 -0
@@ -276,6 +276,12 @@ static struct rds_connection *__rds_conn_create(struct net *net, conn->c_trans = trans; +/* The transport may just have been swapped for loopback; size the +* set of paths - which is also what rds_conn_destroy() tears down +* again - by the transport the connection actually uses. +*/ + npaths = (trans->t_mp_capable ?RDS_MPATH_WORKERS :1 ); +init_waitqueue_head (&conn->c_hs_waitq);for (i =0 ; i < npaths; i++) { __rds_conn_path_init(conn, &conn->c_path[i],
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 215/457 net/smc: fix UAF on lgr list traversal in smcr_port_err()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sidraya Jayagond <[email protected]>
[ Upstream commit 61cb282fe97b3b0ba32ca09417a693162bf4ae3f ]
smcr_port_err() traverses smc_lgr_list.list without holding smc_lgr_list.lock, allowing a concurrent smc_lgr_terminate_sched() to free an lgr while it is still being dereferenced.
Hold smc_lgr_list.lock across the traversal. Update smc_ib_gid_check() to call smcr_port_err() after releasing the lock.
--- net/smc/smc_core.c | 2 ++ net/smc/smc_ib.c | 10 ++++++++-- 2 files changed, 10 insertions(+), 2 deletions(-)
net/smc/smc_core.c +2 -0
@@ -1849,6 +1849,7 @@ void smcr_port_err(struct smc_ib_device *smcibdev, u8 ibport)struct smc_link_group *lgr, *n;int i; +spin_lock_bh (&smc_lgr_list.lock);list_for_each_entry_safe (lgr, n, &smc_lgr_list.list, list) {if (strncmp (smcibdev->pnetid[ibport -1 ], lgr->pnet_id,SMC_MAX_PNETID_LEN ))
@@ -1863,6 +1864,7 @@ void smcr_port_err(struct smc_ib_device *smcibdev, u8 ibport)smcr_link_down_cond_sched (lnk); } } +spin_unlock_bh (&smc_lgr_list.lock); }static void smc_link_down_work (struct work_struct *work)
net/smc/smc_ib.c +8 -2
@@ -333,6 +333,7 @@ static bool smc_ib_check_link_gid(u8 gid[SMC_GID_SIZE], bool smcrv2,static void smc_ib_gid_check (struct smc_ib_device *smcibdev,u8 ibport) {struct smc_link_group *lgr; +bool stale_gid = false;int i;spin_lock_bh (&smc_lgr_list.lock);
@@ -348,11 +349,16 @@ static void smc_ib_gid_check(struct smc_ib_device *smcibdev, u8 ibport)continue ;if (!smc_ib_check_link_gid (lgr->lnk[i].gid, lgr->smc_version ==SMC_V2 , - smcibdev, ibport)) -smcr_port_err (smcibdev, ibport); + smcibdev, ibport)) { + stale_gid = true; + goto out; + } } } +out:spin_unlock_bh (&smc_lgr_list.lock); +if (stale_gid) +smcr_port_err (smcibdev, ibport); }static int smc_ib_remember_port_attr (struct smc_ib_device *smcibdev,u8 ibport)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 216/457 tipc: Fix a data race on mon->peer_cnt in mon_timeout()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ginger Li <[email protected]>
[ Upstream commit 8e1937fed6738460554ec123c64839e2445e7d53 ]
mon_timeout() evaluates dom_size(mon->peer_cnt) before it takes mon->lock, while mon->peer_cnt is updated under that lock by tipc_mon_add_peer() and tipc_mon_remove_peer(). The value can therefore be stale, and the decision whether the local domain has to be recomputed can be based on an outdated member count.
Read mon->peer_cnt inside the write_lock_bh(&mon->lock) protected region.
--- net/tipc/monitor.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
net/tipc/monitor.c +2 -1
@@ -632,9 +632,10 @@ static void mon_timeout(struct timer_list *t) {struct tipc_monitor *mon =timer_container_of (mon, t, timer);struct tipc_peer *self; -int best_member_cnt =dom_size (mon->peer_cnt) -1 ; +int best_member_cnt;write_lock_bh (&mon->lock); + best_member_cnt =dom_size (mon->peer_cnt) -1 ; self = mon->self;if (self && (best_member_cnt != self->applied)) {mon_update_local_domain (mon);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 217/457 net: ethernet: stmmac: dwmac-rk: fix bulk clock leak when the PHY clock fails
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Coia Prant <[email protected]>
[ Upstream commit 8db67bb6a1fffa4df68fbbc22e39943aeeff9178 ]
gmac_clk_enable() enables the bulk clocks first and then the optional PHY clock. If clk_prepare_enable() on the PHY clock fails, the function returns without rolling back the bulk clocks, and bsp_priv->clk_enabled stays false, so the later gmac_clk_enable(bsp_priv, false) becomes a no-op and the bulk clock references are leaked.
Add the missing clk_bulk_disable_unprepare() on that failure path.
--- drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c +4 -1
@@ -1162,8 +1162,11 @@ static int gmac_clk_enable(struct rk_priv_data *bsp_priv, bool enable)return ret; ret =clk_prepare_enable (bsp_priv->clk_phy); -if (ret) +if (ret) { +clk_bulk_disable_unprepare (bsp_priv->num_clks, + bsp_priv->clks);return ret; + }rk_configure_io_clksel (bsp_priv);rk_ungate_rmii_clock (bsp_priv);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 218/457 net: flush skb_defer_nodes in dev_cpu_dead()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit 06e3f54e8b22040ada01a28343badf1990b4dd7d ]
When a CPU goes offline, dev_cpu_dead() drains its softnet queues (completion_queue, output_queue, poll_list, process_queue, and input_pkt_queue), but leaves net_hotdata.skb_defer_nodes untouched.
If oldcpu goes offline while holding pending skbs in its skb_defer_nodes lists (e.g. below the sysctl_skb_defer_max >> 1 IPI threshold, or if the IPI races with CPU teardown), those skbs remain stranded until oldcpu is brought back online. If any of these skbs hold page_pool fragments, page_pool_destroy() will stall indefinitely waiting for inflight pages to be returned when a netdev or driver is torn down while oldcpu is offline.
Additionally, if smp_call_function_single_async() fails in kick_defer_list_purge() because the target CPU went offline, reset defer_ipi_scheduled to 0 so future IPI kicks are not blocked when the CPU comes back online.
Also, if oldcpu was the last online CPU on its NUMA node, drain that node's slot across all CPUs so no skbs deferred from that node remain stranded on idle remote CPUs (or if the node itself is subsequently offlined).
Finally, in skb_attempt_defer_free(), re-check cpu_online(cpu) and whether the caller migrated CPUs after llist_add(), flushing the node list if so, to close the preemption TOCTOU race against CPU/node teardown.
--- net/core/dev.c | 47 +++++++++++++++++++++++++++++++++++------------ net/core/dev.h | 2 ++ net/core/skbuff.c | 12 +++++++++--- 3 files changed, 46 insertions(+), 15 deletions(-)
net/core/dev.c +35 -12
@@ -5370,7 +5370,8 @@ void kick_defer_list_purge(unsigned int cpu)backlog_unlock_irq_restore (sd, flags); }else if (!cmpxchg (&sd->defer_ipi_scheduled,0 ,1 )) { -smp_call_function_single_async (cpu, &sd->defer_csd); +if (smp_call_function_single_async (cpu, &sd->defer_csd)) +WRITE_ONCE (sd->defer_ipi_scheduled,0 ); } }
@@ -6894,25 +6895,35 @@ bool napi_complete_done(struct napi_struct *n, int work_done) }EXPORT_SYMBOL (napi_complete_done); -static void skb_defer_free_flush (void ) +static void __skb_defer_free_flush (struct skb_defer_node *sdn,int budget) {struct llist_node *free_list;struct sk_buff *skb, *next; + +if (llist_empty (&sdn->defer_list)) +return ; +atomic_long_set (&sdn->defer_count,0 ); + free_list =llist_del_all (&sdn->defer_list); + +llist_for_each_entry_safe (skb, next, free_list, ll_node) { +prefetch (next); +napi_consume_skb (skb, budget); + } +} + +void skb_defer_node_flush (struct skb_defer_node *sdn) +{ +__skb_defer_free_flush (sdn,0 ); +} + +static void skb_defer_free_flush (void ) +{struct skb_defer_node *sdn;int node;for_each_node (node) { sdn =this_cpu_ptr (net_hotdata.skb_defer_nodes) + node; - -if (llist_empty (&sdn->defer_list)) -continue ; -atomic_long_set (&sdn->defer_count,0 ); - free_list =llist_del_all (&sdn->defer_list); - -llist_for_each_entry_safe (skb, next, free_list, ll_node) { -prefetch (next); -napi_consume_skb (skb,1 ); - } +__skb_defer_free_flush (sdn,1 ); } }
@@ -12773,6 +12784,7 @@ static int dev_cpu_dead(unsigned int oldcpu)struct sk_buff **list_skb;struct sk_buff *skb;unsigned int cpu; +int node;struct softnet_data *sd, *oldsd, *remsd =NULL ;local_irq_disable ();
@@ -12833,6 +12845,17 @@ static int dev_cpu_dead(unsigned int oldcpu)rps_input_queue_head_incr (oldsd); } +for_each_node (node) +skb_defer_node_flush (per_cpu_ptr (net_hotdata.skb_defer_nodes, + oldcpu) + node); + node =cpu_to_node (oldcpu); +if (node_possible (node) && + !cpumask_intersects (cpumask_of_node (node), cpu_online_mask)) { +for_each_possible_cpu (cpu) +skb_defer_node_flush (per_cpu_ptr (net_hotdata.skb_defer_nodes, + cpu) + node); + } +return 0 ; }
net/core/dev.h +2 -0
@@ -399,6 +399,8 @@ static inline void napi_assert_will_not_race(const struct napi_struct *napi)WARN_ON (READ_ONCE (napi->list_owner) !=-1 ); } +struct skb_defer_node ; +void skb_defer_node_flush (struct skb_defer_node *sdn);void kick_defer_list_purge (unsigned int cpu); int dev_set_hwtstamp_phylib(struct net_device *dev,
net/core/skbuff.c +9 -3
@@ -7348,8 +7348,8 @@ void skb_attempt_defer_free(struct sk_buff *skb)struct skb_defer_node *sdn;unsigned long defer_count;unsigned int defer_max; +int cpu, my_cpu;bool kick; -int cpu;if (static_branch_unlikely (&skb_defer_disable_key)) goto nodefer;
@@ -7359,7 +7359,8 @@ void skb_attempt_defer_free(struct sk_buff *skb) goto nodefer; cpu = skb->alloc_cpu; -if (cpu ==raw_smp_processor_id () || + my_cpu =raw_smp_processor_id (); +if (cpu == my_cpu ||WARN_ON_ONCE (cpu >= nr_cpu_ids) || !cpu_online (cpu)) { nodefer:kfree_skb_napi_cache (skb);
@@ -7370,7 +7371,7 @@ nodefer: kfree_skb_napi_cache(skb);DEBUG_NET_WARN_ON_ONCE (skb->destructor);DEBUG_NET_WARN_ON_ONCE (skb_nfct (skb)); - sdn =per_cpu_ptr (net_hotdata.skb_defer_nodes, cpu) +numa_node_id (); + sdn =per_cpu_ptr (net_hotdata.skb_defer_nodes, cpu) +cpu_to_node (my_cpu); defer_max =READ_ONCE (net_hotdata.sysctl_skb_defer_max); defer_count =atomic_long_inc_return (&sdn->defer_count);
@@ -7380,6 +7381,11 @@ nodefer: kfree_skb_napi_cache(skb);llist_add (&skb->ll_node, &sdn->defer_list); +if (unlikely (!cpu_online (cpu) || my_cpu !=raw_smp_processor_id ())) { +skb_defer_node_flush (sdn); +return ; + } +/* Send an IPI every time queue reaches half capacity. */ kick = (defer_count -1 ) == (defer_max >>1 );
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 219/457 gve: DQO: fix header length used by gve_can_send_tso() for UDP GSO
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit 83769c23fb1879edc916a526ba424285033baf2d ]
gve_can_send_tso() computes how many buffers each segment of a GSO packet would span, and for this it needs the length of the headers that the device replicates in front of every segment.
It unconditionally uses skb_tcp_all_headers(), which reads the doff field of the TCP header. SKB_GSO_UDP_L4 packets have no TCP header:tcp_hdrlen() then reads one byte of the UDP payload, and header_len can be anything in [0, 60] instead of the transport offset plus the eight bytes of the UDP header that gve_prep_tso() programs into the TSO context descriptor.
A wrong header length shifts all the segment boundaries computed in the loop, so the number of buffers per segment can be over or under estimated. In the first case, GSO is needlessly disabled for this packet by gve_features_check_dqo() and the stack has to segment it. In the second case, the driver hands the device a packet whose segments span more than GVE_TX_MAX_DATA_DESCS buffers.
Use the UDP header length for SKB_GSO_UDP_L4 packets, matching what gve_prep_tso() does.
--- drivers/net/ethernet/google/gve/gve_tx_dqo.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-)
drivers/net/ethernet/google/gve/gve_tx_dqo.c +7 -1
@@ -918,13 +918,19 @@ static bool gve_can_send_tso(const struct sk_buff *skb) {const int max_bufs_per_seg =GVE_TX_MAX_DATA_DESCS -1 ;const struct skb_shared_info *shinfo =skb_shinfo (skb); -const int header_len =skb_tcp_all_headers (skb);const int gso_size = shinfo->gso_size;int cur_seg_num_bufs;int prev_frag_size;int cur_seg_size; +int header_len;int i; +/* Must match the header length programmed by gve_prep_tso(). */ +if (skb_is_gso_tcp (skb)) + header_len =skb_tcp_all_headers (skb); +else + header_len =skb_transport_offset (skb) +sizeof (struct udphdr ); + cur_seg_size =skb_headlen (skb) - header_len; prev_frag_size =skb_headlen (skb); cur_seg_num_bufs = cur_seg_size >0 ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 220/457 gve: fix TX drop when GSO MSS is too small for hw
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eddie Phillips <[email protected]>
[ Upstream commit 3b430ea6234087957b0d3cd181e3116722b59819 ]
The device has a strict requirement that the minimum MSS (gso_size) for TSO/GSO packets must be at least 88 bytes. If a packet below this threshold is pushed to the hardware, it can cause hardware to silently drop the packet, leading to increased latency and retransmissions.
Currently, this is validated too late in the transmit pipeline (gve_prep_tso), leading to silent drops.
Fix this by moving the validation into the .ndo_features_check callback (gve_features_check_dqo). If we detect a GSO packet with a gso_size smaller than GVE_TX_MIN_TSO_MSS_DQO, we clear the GSO feature flags for this packet.
--- drivers/net/ethernet/google/gve/gve_tx_dqo.c | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-)
drivers/net/ethernet/google/gve/gve_tx_dqo.c +3 -11
@@ -577,17 +577,6 @@ static int gve_prep_tso(struct sk_buff *skb)int header_len;int err; -/* Note: HW requires MSS (gso_size) to be <= 9728 and the total length -* of the TSO to be <= 262143. -* -* However, we don't validate these because: -* - Hypervisor enforces a limit of 9K MTU -* - Kernel will not produce a TSO larger than 64k -*/ - -if (unlikely (shinfo->gso_size <GVE_TX_MIN_TSO_MSS_DQO )) -return -1 ; -/* Needed because we will modify header. */ err =skb_cow_head (skb,0 );if (err <0 )
@@ -925,6 +914,9 @@ static bool gve_can_send_tso(const struct sk_buff *skb)int header_len;int i; +if (unlikely (gso_size <GVE_TX_MIN_TSO_MSS_DQO )) +return false; +/* Must match the header length programmed by gve_prep_tso(). */ if (skb_is_gso_tcp (skb)) header_len =skb_tcp_all_headers (skb);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 221/457 gve: DQO: reject TSO packets with an out of range MSS
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit 296c83b5ccc808c080865eb20fd7a477b0355bb7 ]
gve_prep_tso() notes that the device requires the MSS to be <= 9728, but does not enforce it, assuming the 9K MTU enforced by the hypervisor and the 64KB limit on TSO sizes are enough.
This does not hold for packets that were not generated locally. A guest behind a tap, or any packet socket user, can provide an arbitrary gso_size in virtio_net_hdr. Layer 2 forwarding does not check the MTU for GSO packets (is_skb_forwardable()), and gso_features_check() only bounds skb->len and gso_segs, never gso_size.
Such a packet reaches gve_tx_fill_tso_ctx_desc(), which puts gso_size into the mss field of the TSO context descriptor. This field is 14 bits wide, so a gso_size of 16384 is silently turned into an MSS of zero.
Drop these packets from gve_prep_tso(), and make sure that gve_features_check_dqo() leaves their GSO bits alone: skb_segment() splits at gso_size regardless of the MTU, so falling back to software segmentation would give the device non TSO packets bigger than the 9728 bytes it supports.
Note that the device can still be given oversized non TSO packets when the stack segments in software for other reasons, for instance after TSO has been disabled with ethtool. This is a generic issue, because the MTU check is skipped for GSO packets in the forwarding path, and is addressed separately.
--- .../net/ethernet/google/gve/gve_desc_dqo.h | 5 ++++ drivers/net/ethernet/google/gve/gve_tx_dqo.c | 26 ++++++++++++++++++- 2 files changed, 30 insertions(+), 1 deletion(-)
drivers/net/ethernet/google/gve/gve_desc_dqo.h +5 -0
@@ -14,6 +14,11 @@#define GVE_TX_MAX_HDR_SIZE_DQO 255#define GVE_TX_MIN_TSO_MSS_DQO 88 +/* HW limit. This also has to fit in the 14 bits of the mss field of +* struct gve_tx_tso_context_desc_dqo. +*/ +#define GVE_TX_MAX_TSO_MSS_DQO 9728 +#ifndef __LITTLE_ENDIAN_BITFIELD#error "Only little endian supported"#endif
drivers/net/ethernet/google/gve/gve_tx_dqo.c +25 -1
@@ -577,6 +577,20 @@ static int gve_prep_tso(struct sk_buff *skb)int header_len;int err; +/* Note: HW requires the total length of the TSO to be <= 262143, +* this is enforced by netif_set_tso_max_size(). +* +* MSS (gso_size) can not be trusted: packets forwarded from a tap or +* injected by a packet socket can carry an arbitrary value, while the +* mss field of the TSO context descriptor is only 14 bits wide. +* +* A too big MSS is dropped here instead of being rejected from +* gve_features_check_dqo(), because software segmentation would +* produce packets larger than the device can send. +*/ +if (unlikely (shinfo->gso_size >GVE_TX_MAX_TSO_MSS_DQO )) +return -1 ; +/* Needed because we will modify header. */ err =skb_cow_head (skb,0 );if (err <0 )
@@ -964,7 +978,17 @@ netdev_features_t gve_features_check_dqo(struct sk_buff *skb, struct net_device *dev, netdev_features_t features) { - if (skb_is_gso(skb) && !gve_can_send_tso(skb)) +if (!skb_is_gso (skb)) +return features; + +/* Keep the GSO bits for a too big MSS, so that gve_prep_tso() drops +* the packet: software segmentation would give packets larger than +* the device can send. +*/ +if (skb_shinfo (skb)->gso_size >GVE_TX_MAX_TSO_MSS_DQO ) +return features; + +if (!gve_can_send_tso (skb))return features & ~NETIF_F_GSO_MASK ;return features;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 222/457 llc: fix skb UAF and leaks on llc_mac_hdr_init() failure
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit 72f9dd522f8d6c5a00be9695c7bb74631eb5069e ]
In llc_conn_ac_resend_i_xxx_x_set_0_or_send_rr(), if llc_mac_hdr_init() fails, kfree_skb(skb) is called instead of kfree_skb(nskb). This leaks the newly allocated nskb, reads from the freed skb via LLC_I_GET_NR(pdu), and double-frees skb when llc_conn_state_process() drops its reference.
In llc_sap_action_send_xid_r() and llc_sap_action_send_test_r(), nskb is leaked if llc_mac_hdr_init() returns an error.
Free nskb in all three error paths.
--- net/llc/llc_c_ac.c | 2 +- net/llc/llc_s_ac.c | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-)
net/llc/llc_c_ac.c +1 -1
@@ -437,7 +437,7 @@ int llc_conn_ac_resend_i_xxx_x_set_0_or_send_rr(struct sock *sk,if (likely (!rc))llc_conn_send_pdu (sk, nskb);else -kfree_skb (skb); +kfree_skb (nskb); }if (rc) { nr =LLC_I_GET_NR (pdu);
net/llc/llc_s_ac.c +4 -0
@@ -121,6 +121,8 @@ int llc_sap_action_send_xid_r(struct llc_sap *sap, struct sk_buff *skb) rc =llc_mac_hdr_init (nskb, mac_sa, mac_da);if (likely (!rc)) rc =dev_queue_xmit (nskb); +else +kfree_skb (nskb); out:return rc; }
@@ -170,6 +172,8 @@ int llc_sap_action_send_test_r(struct llc_sap *sap, struct sk_buff *skb) rc =llc_mac_hdr_init (nskb, mac_sa, mac_da);if (likely (!rc)) rc =dev_queue_xmit (nskb); +else +kfree_skb (nskb); out:return rc; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 223/457 bridge: check llc_mac_hdr_init() return value in br_send_bpdu()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit ac704ff08e511c87643799c385f55ecd69b85e03 ]
If llc_mac_hdr_init() fails (for instance if the port device type does not support LLC or dev_hard_header() fails), br_send_bpdu() should drop the skb instead of resetting the mac header to the LLC payload and transmitting a malformed frame.
--- net/bridge/br_stp_bpdu.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
net/bridge/br_stp_bpdu.c +4 -1
@@ -52,7 +52,10 @@ static void br_send_bpdu(struct net_bridge_port *p, LLC_SAP_BSPAN, LLC_PDU_CMD);llc_pdu_init_as_ui_cmd (skb); -llc_mac_hdr_init (skb, p->dev->dev_addr, p->br->group_addr); +if (llc_mac_hdr_init (skb, p->dev->dev_addr, p->br->group_addr)) { +kfree_skb (skb); +return ; + }skb_reset_mac_header (skb);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 224/457 net/sched: sch_teql: fix shadowed err in __teql_resolve()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit 907b978e82cb4c1c245fc2985bb27c5d5c88c8f6 ]
__teql_resolve() declares an inner 'int err;' inside the 'if (neigh_event_send(n, skb_res) == 0)' block, shadowing the outer 'int err = 0;'. As a result, a negative return from dev_hard_header() is written to the inner variable and __teql_resolve() still returns 0.
Remove the shadowed variable and set the outer err to -EINVAL when dev_hard_header() returns a negative error.
--- net/sched/sch_teql.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-)
net/sched/sch_teql.c +2 -5
@@ -265,14 +265,11 @@ __teql_resolve(struct sk_buff *skb, struct sk_buff *skb_res, }if (neigh_event_send (n, skb_res) ==0 ) { -int err;char haddr[MAX_ADDR_LEN ];neigh_ha_snapshot (haddr, n, dev); - err =dev_hard_header (skb, dev,ntohs (skb_protocol (skb, false)), - haddr,NULL , skb->len); - -if (err <0 ) +if (dev_hard_header (skb, dev,ntohs (skb_protocol (skb, false)), + haddr,NULL , skb->len) <0 ) err = -EINVAL ; }else { err = (skb_res ==NULL ) ? -EAGAIN :1 ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 225/457 vlan: ensure sufficient headroom in vlan_dev_hard_header()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit cd5dd68267c4238795fadaf02b3575ca3f8a6500 ]
Callers that only reserve ETH_HLEN or less (such as llc_alloc_frame()), or skbs allocated before dynamic device/headroom changes (e.g. toggling VLAN_FLAG_REORDER_HDR or bonding/team switching slaves), can reach vlan_dev_hard_header() with insufficient headroom and trigger skb_under_panic().
Use skb_cow_head() in vlan_dev_hard_header() when VLAN_FLAG_REORDER_HDR is not set to ensure sufficient headroom for the VLAN header(s) and the underlying device hard header.
Use READ_ONCE() to read dev->hard_header_len and dev->needed_headroom as they can be updated concurrently under RTNL (e.g. in vlan_transfer_features()) while vlan_dev_hard_header() runs locklessly on the transmit path. Also avoid LL_RESERVED_SPACE(dev) here so that the extra HH_DATA_MOD alignment padding does not trigger unnecessary pskb_expand_head() reallocations on inner stacked VLAN devices after the outer VLAN header has been pushed.
--- net/8021q/vlan_dev.c | 5 +++++ 1 file changed, 5 insertions(+)
net/8021q/vlan_dev.c +5 -0
@@ -55,6 +55,11 @@ static int vlan_dev_hard_header(struct sk_buff *skb, struct net_device *dev,int rc;if (!(vlan->flags &VLAN_FLAG_REORDER_HDR )) { +unsigned int hlen =READ_ONCE (dev->hard_header_len) + +READ_ONCE (dev->needed_headroom); + +if (skb_cow_head (skb, hlen) <0 ) +return -ENOMEM ; vhdr =skb_push (skb,VLAN_HLEN ); vlan_tci = vlan->vlan_id;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 226/457 ovl: fix UAF in ovl_do_mkdir() debug print
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Amir Goldstein <[email protected]>
[ Upstream commit ae146bc1abdeb4607abf2975b858c053024e8ac1 ]
ovl_do_mkdir() prints the input dentry with %pd after vfs_mkdir(). Since commit fe497f0759e0 ("VFS: change vfs_mkdir() to unlock on failure."), vfs_mkdir() calls end_creating() on the input dentry on failure and may replace it on success, so the post-call %pd can use-after-free the dentry when CONFIG_OVERLAY_FS_DEBUG is enabled.
Print the dentry before the call and only the result afterward.
--- fs/overlayfs/overlayfs.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
fs/overlayfs/overlayfs.h +3 -1
@@ -254,8 +254,10 @@ static inline struct dentry *ovl_do_mkdir(struct ovl_fs *ofs, {struct dentry *ret; +/* vfs_mkdir() drops @dentry on failure and may replace it on success */ +pr_debug ("mkdir(%pd2, 0%o)\n" , dentry, mode); ret =vfs_mkdir (ovl_upper_mnt_idmap (ofs), dir, dentry, mode,NULL ); -pr_debug ("mkdir(%pd2, 0%o) = %i\n" , dentry, mode,PTR_ERR_OR_ZERO (ret)); +pr_debug ("...mkdir = %i\n" ,PTR_ERR_OR_ZERO (ret));return ret; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 227/457 autofs: fix sbi->pipe file reference leak in autofs_kill_sb()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hui Peng <[email protected]>
[ Upstream commit aa5e44b29ffe4eaa08cc2237fd65bc2596bc023e ]
When autofs_fill_super() fails before clearing AUTOFS_SBI_CATATONIC (for example, when find_get_pid() fails on an invalid pgrp mount option, or when an fs_context is closed before mounting), deactivate_locked_super() invokes autofs_kill_sb() -> autofs_catatonic_mode(sbi).
Because AUTOFS_SBI_CATATONIC is still set in sbi->flags, autofs_catatonic_mode() returns early without calling fput(sbi->pipe), permanently leaking the pipe struct file reference.
Explicitly release sbi->pipe in autofs_kill_sb() if it is still non-NULL after autofs_catatonic_mode().
--- fs/autofs/inode.c | 4 ++++ 1 file changed, 4 insertions(+)
fs/autofs/inode.c +4 -0
@@ -51,6 +51,10 @@ void autofs_kill_sb(struct super_block *sb)if (sbi) {/* Free wait queues, close pipe */ autofs_catatonic_mode (sbi); +if (sbi->pipe) { +fput (sbi->pipe); + sbi->pipe =NULL ; + }put_pid (sbi->oz_pgrp); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 228/457 perf/x86/intel: Ensure KVM guest PEBS path doesnt set unwanted PERF_GLOBAL_CTRL bits
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sean Christopherson <[email protected]>
[ Upstream commit cec38d5c098a350dcf084d345025136ade7e6d1e ]
When reinstating PEBS counters into PERF_GLOBAL_CTRL for a KVM guest, mask the value with perf's desired/original PERF_GLOBAL_CTRL value to ensure KVM doesn't unintentionally set reserved bits in PERF_GLOBAL_CTRL. E.g. if the guest's PEBS_ENABLE value had bit 63, "Enable Precise Store", set, then using the raw guest PEBS value would propagate bit 63 to the guest's PERF_GLOBAL_CTRL value (which thankfully would be a failed VM-Entry, not a VMX Abort).
The only reason this bug isn't reachable is because KVM doesn't support "Enable Precise Store" (which is probably a KVM bug?), i.e. bit 63 can't be set in kvm_pmu->pebs_enable and thus not in arr[pebs_enable].guest. In other words, this _should_ be a glorified NOP in the current code base.
--- arch/x86/events/intel/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
arch/x86/events/intel/core.c +1 -1
@@ -5361,7 +5361,7 @@ static struct perf_guest_switch_msr *intel_guest_get_msrs(int *nr, void *data) arr[pebs_enable].guest &= ~kvm_pmu->host_cross_mapped_mask; arr[global_ctrl].guest &= ~kvm_pmu->host_cross_mapped_mask;/* Set hw GLOBAL_CTRL bits for PEBS counter when it runs for guest */ - arr[global_ctrl].guest |= arr[pebs_enable].guest; + arr[global_ctrl].guest |= intel_ctrl & arr[pebs_enable].guest; }return arr;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 229/457 perf/x86/intel: Dont write PEBS_ENABLED on host<=>guest xfers if CPU has PEBS isolation, to fix stuck PEBS_ENABLED
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sean Christopherson <[email protected]>
[ Upstream commit 4b64dbdc5861477f148e13d1ed127e7fe7182e4f ]
When filling the list of MSRs to be loaded by KVM on VM-Enter and VM-Exit, *never* insert an entry for PEBS_ENABLED if the CPU properly isolates PEBS events, in which case disabling counters via PERF_GLOBAL_CTRL is sufficient to prevent unwanted PEBS events in the guest (or host). Because perf loads PEBS_ENABLE with the unfiltered cpu_hw_events.pebs_enabled, i.e. with both host and guest masks, there is no need to load different values for the guest versus host, perf+KVM can and should simply control which counters are enabled/disabled via PERF_GLOBAL_CTRL.
Avoiding touching PEBS_ENABLED "fixes" a bug where PEBS_ENABLED can end up with "stuck" bits if a PEBS event is throttled between generating the list and actually entering the guest (Intel CPUs can't arbtitrarily block NMIs). Fixes in quotes because leaving PEBS_ENABLED as-is doesn't fix the underlying problem of perf (via PMIs) being able to modify state after the perf<=>KVM handoff.
But not writing PEBS_ENABLED is desirable no matter what, as stating the obvious, leaving PEBS_ENABLED as-is avoids three MSR writes on every VMX
transition: one each on entry/exit, and one more explicit WRMSR to zero PEBS_ENABLED before VM-Entry (KVM assumes the only reason PEBS_ENABLED is in the load list is if the CPU lacks PEBS isolation and thus needs a quiescent period).
Opportunistically add comments to (better) explain the rules for generating the set of PEBS counters that will be active while the guest is running, along with a FIXME for the suspected hack-a-fix where perf disables guest PEBS if _any_ PEBS event is configured to count in the host (commit 854250329c02 ("KVM: x86/pmu: Disable guest PEBS temporarily in two rare situations") doesn't explain the motivation, at all).
--- arch/x86/events/intel/core.c | 55 ++++++++++++++++++++++++------------ 1 file changed, 37 insertions(+), 18 deletions(-)
arch/x86/events/intel/core.c +37 -18
@@ -5294,12 +5294,15 @@ static struct perf_guest_switch_msr *intel_guest_get_msrs(int *nr, void *data)struct kvm_pmu *kvm_pmu = (struct kvm_pmu *)data;u64 intel_ctrl =hybrid (cpuc->pmu, intel_ctrl);u64 pebs_mask = cpuc->pebs_enabled & x86_pmu.pebs_capable; -int global_ctrl, pebs_enable; +u64 guest_pebs_mask; +int global_ctrl;/* * In addition to obeying exclude_guest/exclude_host, remove bits being * used for PEBS when running a guest, because PEBS writes to virtual -* addresses (not physical addresses). +* addresses (not physical addresses). If the guest wants to utilize +* PEBS, and PEBS can be safely enabled in the guest, bits for the guest's +* PEBS-enabled counters will be OR'd back in as appropriate. */ *nr =0 ; global_ctrl = (*nr)++;
@@ -5346,24 +5349,40 @@ static struct perf_guest_switch_msr *intel_guest_get_msrs(int *nr, void *data) }; } - pebs_enable = (*nr)++; - arr[pebs_enable] = (struct perf_guest_switch_msr ){ - .msr =MSR_IA32_PEBS_ENABLE , - .host = cpuc->pebs_enabled & ~cpuc->intel_ctrl_guest_mask, - .guest = pebs_mask & ~cpuc->intel_ctrl_host_mask & kvm_pmu->pebs_enable, - }; +/* +* Restrict guest PEBS events to counters that (a) perf supports, (b) +* the guest wants to use for PEBS, (c) are not excluded from counting +* in the guest, and (d) _are_ excluded from counting in the host. +*/ + guest_pebs_mask = pebs_mask & intel_ctrl & kvm_pmu->pebs_enable & + ~cpuc->intel_ctrl_host_mask & + cpuc->intel_ctrl_guest_mask; -if (arr[pebs_enable].host) { -/* Disable guest PEBS if host PEBS is enabled. */ - arr[pebs_enable].guest =0 ; - }else { -/* Disable guest PEBS thoroughly for cross-mapped PEBS counters. */ - arr[pebs_enable].guest &= ~kvm_pmu->host_cross_mapped_mask; - arr[global_ctrl].guest &= ~kvm_pmu->host_cross_mapped_mask; -/* Set hw GLOBAL_CTRL bits for PEBS counter when it runs for guest */ - arr[global_ctrl].guest |= intel_ctrl & arr[pebs_enable].guest; - } +/* +* Disable counters where the guest PMC is different than the host PMC +* being used on behalf of the guest, as the PEBS record includes +* PERF_GLOBAL_STATUS, i.e. the guest will see overflow status for the +* wrong counter(s). +*/ + guest_pebs_mask &= ~kvm_pmu->host_cross_mapped_mask; + +/* +* FIXME: Allow guest and host usage of PEBS events to co-exist instead +* of disabling guest PEBS entirely if the host is using PEBS. +* What exactly goes wrong if guest and host are using PEBS is +* unknown. +*/ +if (pebs_mask & ~cpuc->intel_ctrl_guest_mask) + guest_pebs_mask =0 ; +/* +* Do NOT mess with PEBS_ENABLED. As above, disabling counters via +* PERF_GLOBAL_CTRL is sufficient, and loading a stale PEBS_ENABLED, +* e.g. on VM-Exit, can put the system in a bad state. Simply enable +* counters in PERF_GLOBAL_CTRL, as perf load PEBS_ENABLED with the +* full value, i.e. perf *also* relies on PERF_GLOBAL_CTRL. +*/ + arr[global_ctrl].guest |= guest_pebs_mask;return arr; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 230/457 perf/x86/intel: Dont pointlessly context switch DS_AREA (and PEBS config) if PEBS is unused
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sean Christopherson <[email protected]>
[ Upstream commit d06260e99eb93d2942b7af4ccd789eb8a6c829d3 ]
When filling the list of MSRs to be loaded by KVM on VM-Enter and VM-Exit, load the guest values for DS_AREA and (conditionally) MSR_PEBS_DATA_CFG if and only if PEBS will be active in the guest, i.e. only if a PEBS record may be generated while running the guest. As shown by the !pebs_ept path, it's perfectly safe to run with the host's DS_AREA, so long as PEBS-enabled counters are disabled via PERF_GLOBAL_CTRL.
Omitting DS_AREA and MSR_PEBS_DATA_CFG when PEBS is unused saves two MSR writes per MSR on each VMX transition, i.e. eliminates two/four pointless MSR writes on each VMX roundtrip when PEBS isn't being used by the guest.
--- arch/x86/events/intel/core.c | 39 +++++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 14 deletions(-)
arch/x86/events/intel/core.c +25 -14
@@ -5332,23 +5332,14 @@ static struct perf_guest_switch_msr *intel_guest_get_msrs(int *nr, void *data)return arr; } +/* +* If the guest won't use PEBS or the CPU doesn't support PEBS in the +* guest, then there's nothing more to do as disabling PMCs via +* PERF_GLOBAL_CTRL is sufficient on CPUs with guest/host isolation. +*/ if (!kvm_pmu || !x86_pmu.pebs_ept)return arr; - arr[(*nr)++] = (struct perf_guest_switch_msr ){ - .msr =MSR_IA32_DS_AREA , - .host = (unsigned long )cpuc->ds, - .guest = kvm_pmu->ds_area, - }; - -if (x86_pmu.intel_cap.pebs_baseline) { - arr[(*nr)++] = (struct perf_guest_switch_msr ){ - .msr =MSR_PEBS_DATA_CFG , - .host = cpuc->active_pebs_data_cfg, - .guest = kvm_pmu->pebs_data_cfg, - }; - } -/* * Restrict guest PEBS events to counters that (a) perf supports, (b) * the guest wants to use for PEBS, (c) are not excluded from counting
@@ -5375,6 +5366,26 @@ static struct perf_guest_switch_msr *intel_guest_get_msrs(int *nr, void *data)if (pebs_mask & ~cpuc->intel_ctrl_guest_mask) guest_pebs_mask =0 ; +/* +* Context switch DS_AREA and PEBS_DATA_CFG if and only if PEBS will be +* active in the guest; if no records will be generated while the guest +* is running, then simply keep the host values resident in hardware. +*/ + arr[(*nr)++] = (struct perf_guest_switch_msr ){ + .msr =MSR_IA32_DS_AREA , + .host = (unsigned long )cpuc->ds, + .guest = guest_pebs_mask ? kvm_pmu->ds_area : (unsigned long )cpuc->ds, + }; + +if (x86_pmu.intel_cap.pebs_baseline) { + arr[(*nr)++] = (struct perf_guest_switch_msr ){ + .msr =MSR_PEBS_DATA_CFG , + .host = cpuc->active_pebs_data_cfg, + .guest = guest_pebs_mask ? kvm_pmu->pebs_data_cfg : + cpuc->active_pebs_data_cfg, + }; + } +/* * Do NOT mess with PEBS_ENABLED. As above, disabling counters via * PERF_GLOBAL_CTRL is sufficient, and loading a stale PEBS_ENABLED,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 231/457 perf/x86/intel: Make @data a mandatory param for intel_guest_get_msrs()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sean Christopherson <[email protected]>
[ Upstream commit a391618e1d563f099e4c2a704f45d08329ccdf7c ]
Drop "support" for passing a NULL @data/@kvm_pmu param when getting guest MSRs. KVM, the only in-tree user, unconditionally passes a non-NULL pointer, and carrying code that suggests @data may be NULL is confusing, e.g. incorrectly implies that there are scenarios where KVM doesn't pass a PMU context.
--- arch/x86/events/intel/core.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
arch/x86/events/intel/core.c +4 -4
@@ -5333,11 +5333,11 @@ static struct perf_guest_switch_msr *intel_guest_get_msrs(int *nr, void *data) }/* -* If the guest won't use PEBS or the CPU doesn't support PEBS in the -* guest, then there's nothing more to do as disabling PMCs via -* PERF_GLOBAL_CTRL is sufficient on CPUs with guest/host isolation. +* If the CPU doesn't support PEBS in the guest, then there's nothing +* more to do as disabling PMCs via PERF_GLOBAL_CTRL is sufficient on +* CPUs with guest/host isolation. */ -if (!kvm_pmu || !x86_pmu.pebs_ept) +if (!x86_pmu.pebs_ept)return arr;/*
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 232/457 perf/x86/intel: Delete dead NVL PEBS data-source initcall
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Dapeng Mi <[email protected]>
[ Upstream commit 858b37ca19d3f695f7fa94cd14be5a856fd8e7d7 ]
Nova Lake now uses the OMR data-source table for PEBS data-source decoding and no longer depends on the legacy static pebs_data_source[] mapping.
Remove the dead intel_pmu_pebs_data_source_lnl() initialization call for NVL.
--- arch/x86/events/intel/core.c | 2 -- 1 file changed, 2 deletions(-)
arch/x86/events/intel/core.c +0 -2
@@ -8785,8 +8785,6 @@ __init int intel_pmu_init(void)/* Initialize Atom core specific PerfMon capabilities.*/ pmu = &x86_pmu.hybrid_pmu[X86_HYBRID_PMU_ATOM_IDX ];intel_pmu_init_arw (&pmu->pmu); - -intel_pmu_pebs_data_source_lnl ();break ;default :
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 233/457 perf/core: Fix a refcount leak in attach_perf_ctx_data()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Namhyung Kim <[email protected]>
[ Upstream commit cca4980630b3c7a85f53cb43c6018184ce5d4e37 ]
The attach_perf_ctx_data() can race on global and !global cases. The global case is protected by global_ctx_data_rwsem and shares a single reference count using perf_ctx_data.global field.
But when it races with !global case, it may miss to set the global field and result in a reference count leak.
CPU1 CPU2
----------------------------------------------------------------
attach_task_ctx_data(.global=1) attach_task_ctx_data(.global=0)
cd1 = alloc_perf_ctx_data(); cd2 = alloc_perf_ctx_data();
// { .global = 0, .refcount = 1 };
try_cmpxchg(); // success,
// task->perf_ctx_data = cd2
try_cmpxhg(); // fail; old = cd2
refcount_inc_not_zero(&old->refcount); // success
// old.refcount = 2 free_perf_ctx_data(cd1);
Then later detach_global_ctx_data() will see the data but it's not marked as global, so it won't call detach_task_ctx_data().
--- kernel/events/core.c | 2 ++ 1 file changed, 2 insertions(+)
kernel/events/core.c +2 -0
@@ -5454,6 +5454,8 @@ attach_task_ctx_data(struct task_struct *task, struct kmem_cache *ctx_cache, }if (refcount_inc_not_zero (&old->refcount)) { +if (global) + old->global = true;free_perf_ctx_data (cd);/* unused */ return 0 ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 234/457 perf/core: Fill branch entries with a single assignment
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Puranjay Mohan <[email protected]>
[ Upstream commit 24b620729e53d978b3e425f55bc66efd3bab1f59 ]
perf_clear_branch_entry_bitfields() clears the bitfields of struct perf_branch_entry one by one and leaves from/to alone, since callers overwrite those straight away. The list has to be kept in sync with the struct by hand and has already fallen behind: new_type and priv were added to perf_branch_entry and never added here.
Only BRBE writes those two, and neither for every record. brbe_set_perf_entry_type() leaves new_type alone for a branch type it does not recognise, and priv is not set for source-only records. arm_pmuv3.c allocates the per-CPU branch stack with kmalloc(), so such a record reaches userspace with whatever the slot held: uninitialised kmalloc() data on the first pass over the buffer, the previous record's values after that. Nothing under arch/x86/events/ writes either field, so only arm64 is affected.
Assign the whole entry at each site instead. Everything not named is then zero, and there is no list to keep in sync. The bitfields add up to exactly 64 bits, so the struct has no padding to leave undefined.
perf_clear_branch_entry_bitfields() has no callers left, so remove it. perf_entry_from_brbe_regset() assigns an empty literal instead, since it fills from/to conditionally. PERF_BR_SPEC_NA is 0, so dropping the explicit spec assignment changes nothing.
--- arch/x86/events/amd/brs.c | 9 +++-- arch/x86/events/amd/lbr.c | 16 ++++----- arch/x86/events/intel/lbr.c | 65 ++++++++++++++++++++----------------- drivers/perf/arm_brbe.c | 2 +- include/linux/perf_event.h | 17 ---------- 5 files changed, 48 insertions(+), 61 deletions(-)
arch/x86/events/amd/brs.c +4 -5
@@ -343,11 +343,10 @@ void amd_brs_drain(void)if (!amd_brs_match_plm (event, from, to))continue ; -perf_clear_branch_entry_bitfields (br+nr); - - br[nr].from = from; - br[nr].to = to; - + br[nr] = (struct perf_branch_entry ){ + .from = from, + .to = to, + }; nr++; } empty:
arch/x86/events/amd/lbr.c +8 -8
@@ -184,13 +184,6 @@ void amd_pmu_lbr_read(void) entry.to.split.reserved)continue ; -perf_clear_branch_entry_bitfields (br + out); - - br[out].from =sign_ext_branch_ip (entry.from.split.ip); - br[out].to =sign_ext_branch_ip (entry.to.split.ip); - br[out].mispred = entry.from.split.mispredict; - br[out].predicted = !br[out].mispred; -/* * Set branch speculation information using the status of * the valid and spec bits.
@@ -208,7 +201,14 @@ void amd_pmu_lbr_read(void)* speculative and took the correct path */ idx = (entry.to.split.valid <<1 ) | entry.to.split.spec; - br[out].spec = lbr_spec_map[idx]; + + br[out] = (struct perf_branch_entry ){ + .from =sign_ext_branch_ip (entry.from.split.ip), + .to =sign_ext_branch_ip (entry.to.split.ip), + .mispred = entry.from.split.mispredict, + .predicted = !entry.from.split.mispredict, + .spec = lbr_spec_map[idx], + }; out++; }
arch/x86/events/intel/lbr.c +35 -30
89 lines not on this page · raw message
drivers/perf/arm_brbe.c +1 -1
@@ -604,7 +604,7 @@ static bool perf_entry_from_brbe_regset(int index, struct perf_branch_entry *entreturn false; brbinf = bregs.brbinf; -perf_clear_branch_entry_bitfields (entry); + *entry = (struct perf_branch_entry ){ };if (brbe_record_is_complete (brbinf)) { entry->from = bregs.brbsrc; entry->to = bregs.brbtgt;
include/linux/perf_event.h +0 -17
@@ -1467,23 +1467,6 @@ static inline u32 perf_sample_data_size(struct perf_sample_data *data,return size; } -/* -* Clear all bitfields in the perf_branch_entry. -* The to and from fields are not cleared because they are -* systematically modified by caller. -*/ -static inline void perf_clear_branch_entry_bitfields (struct perf_branch_entry *br) -{ - br->mispred =0 ; - br->predicted =0 ; - br->in_tx =0 ; - br->abort =0 ; - br->cycles =0 ; - br->type =0 ; - br->spec =PERF_BR_SPEC_NA ; - br->reserved =0 ; -} - extern void perf_output_sample(struct perf_output_handle *handle, struct perf_event_header *header, struct perf_sample_data *data,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 235/457 sched/core: Account PSI IRQ time to the execution context, not the scheduling context
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zhan Xusheng <[email protected]>
[ Upstream commit a0bb6fac53fa7cf1cadb487b43d4c9276a6b82e3 ]
psi_account_irqtime() has two callers which share rq->psi_irq_time, and they disagree about the context: __schedule() passes the outgoing rq->curr, sched_tick() passes rq->donor. Under proxy execution the donor is blocked on a mutex while rq->curr burns the CPU.
The tick charges PSI_IRQ_FULL to the donor's cgroup and advances the timestamp, so the call from __schedule() then finds delta <= 0 and charges nothing. The delta is not counted twice, it lands on the wrong cgroup.
Pass rq->curr, which is what the call read before commit af0c8b2bf67b ("sched: Split scheduler and execution contexts") renamed 'curr' to 'donor' across sched_tick(). Without CONFIG_SCHED_PROXY_EXEC the two rq members are a union, so this only changes anything where that option is set, and it depends on EXPERT.
--- kernel/sched/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
kernel/sched/core.c +1 -1
@@ -5802,7 +5802,7 @@ void sched_tick(void) curr = rq->curr; donor = rq->donor; -psi_account_irqtime (rq, donor,NULL ); +psi_account_irqtime (rq, curr,NULL );update_rq_clock (rq); hw_pressure =arch_scale_hw_pressure (cpu_of (rq));
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 236/457 mptcp: return sk_wait_data() errors from recvmsg()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mark Amirkan <[email protected]>
[ Upstream commit 60404266ef3e0a1cd8f7a164060e0c83efb72f4b ]
Commit 581302298524 ("mptcp: error out earlier on disconnect") made mptcp_recvmsg() stop when sk_wait_data() returns an error. The error is stored in err, but the function then jumps to a path which returns copied. When no data was copied, recvmsg() therefore returns zero and reports a false EOF.
Store the result in copied, which is the value returned by the function. This also keeps the usual partial-read result when data was copied before the error.
A recvmsg() blocked in one thread reproduces the issue when another thread disconnects the same MPTCP socket with connect(AF_UNSPEC). Before this change recvmsg() returns zero; afterwards it returns -EPIPE.
--- net/mptcp/protocol.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/mptcp/protocol.c +1 -1
@@ -2401,7 +2401,7 @@ static int mptcp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,mptcp_cleanup_rbuf (msk, copied); err =sk_wait_data (sk, &timeo, last);if (err <0 ) { - err = copied ? : err; + copied = copied ? : err; goto out_err; } }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 237/457 drm/pagemap: dma-unmap pages before handling migration errors
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Matthew Brost <[email protected]>
[ Upstream commit 9e6372ec2a3990662ae0a67f56ac0aee19848d5b ]
drm_pagemap_migrate_unmap_pages() relies on the pages array to determine which pages require DMA unmapping. However, drm_pagemap_migration_unlock_put_pages() clears the array as part of its cleanup, leaving drm_pagemap_migrate_unmap_pages() with no valid page information if it is called afterward.
Call drm_pagemap_migrate_unmap_pages() before drm_pagemap_migration_unlock_put_pages() so the pages array remains valid during DMA unmapping.
--- drivers/gpu/drm/drm_pagemap.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-)
drivers/gpu/drm/drm_pagemap.c +6 -6
@@ -1368,13 +1368,13 @@ int drm_pagemap_evict_to_ram(struct drm_pagemap_devmem *devmem_allocation) goto err_finalize; err_finalize: +drm_pagemap_migrate_unmap_pages (devmem_allocation->dev, pagemap_addr, dst, npages, +DMA_FROM_DEVICE , &state);if (err)drm_pagemap_migration_unlock_put_pages (npages, dst);migrate_device_pages (src, dst, npages);drm_pagemap_retire_migrated_pages (src, npages);migrate_device_finalize (src, dst, npages); -drm_pagemap_migrate_unmap_pages (devmem_allocation->dev, pagemap_addr, dst, npages, -DMA_FROM_DEVICE , &state); err_free:kvfree (buf);
@@ -1494,15 +1494,15 @@ static int __drm_pagemap_migrate_to_ram(struct vm_area_struct *vas, goto err_finalize; err_finalize: +if (dev) +drm_pagemap_migrate_unmap_pages (dev, pagemap_addr, migrate.dst, + npages,DMA_FROM_DEVICE , + &state);if (err)drm_pagemap_migration_unlock_put_pages (npages, migrate.dst);migrate_vma_pages (&migrate);drm_pagemap_retire_migrated_pages (migrate.src, npages);migrate_vma_finalize (&migrate); -if (dev) -drm_pagemap_migrate_unmap_pages (dev, pagemap_addr, migrate.dst, - npages,DMA_FROM_DEVICE , - &state); err_free:kvfree (buf); err_out:
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 238/457 vlan: require the MAC header to be present in __vlan_insert_inner_tag()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xiang Mei <[email protected]>
commit ab888242fce4f16f6c4d4c6ec53939ad36aa3b3a upstream.
__vlan_insert_inner_tag() only guarantees head room via skb_cow_head(), never that mac_len bytes of MAC header are present. Its ETH_HLEN wrappers - __vlan_insert_tag() under skb_vlan_push(), and vlan_insert_tag() under validate_xmit_vlan() on the generic transmit path - therefore rewrite the first 16 bytes at skb->data: a 12-byte memmove plus two 2-byte stores at +12 and +14. No caller supplies the bound, while the pop helpers use skb_ensure_writable()/pskb_may_pull().
An IFF_TUN device has hard_header_len == 0, so packet_snd() accepts a one-byte AF_PACKET/SOCK_RAW frame. The first vlan push only sets a hwaccel tag; the next - clsact "action vlan push" or bpf_skb_vlan_push() - enters the helper with skb->len still 1. The head comes from skbuff_small_head without __GFP_ZERO, so each push drags bytes from beyond skb->tail into the frame. After three the one-byte send leaves as 13 bytes carrying 11 bytes of uninitialised slab:
0000: 5a b3 62 12 80 88 ff ff 00 b3 62 12 81
`------------------------------'
only 0x5a was sent; the rest is slab, here the top 56 bits of a linear-map address
Require the MAC header the helper rewrites to be present, so such a frame is dropped rather than transmitted.
--- include/linux/if_vlan.h | 3 +++ 1 file changed, 3 insertions(+)
include/linux/if_vlan.h +3 -0
@@ -365,6 +365,9 @@ static inline int __vlan_insert_inner_taconst u8 meta_len = mac_len >ETH_TLEN ?skb_metadata_len (skb) :0 ;struct vlan_ethhdr *veth; +if (unlikely (!pskb_may_pull (skb, mac_len))) +return -EINVAL ; +if (skb_cow_head (skb, meta_len +VLAN_HLEN ) <0 )return -ENOMEM ;
Greg Kroah-Hartman
maintainerPATCH 239/457 x86/mce: Fix hardware debug register corruption on task migration
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Masami Hiramatsu (Google) <[email protected]>
commit b8d1d5b63a8ef532038eebd9d97d406860385668 upstream.
In exc_machine_check_user(), local_db_save() and local_db_restore() are invoked in the outer entry stubs (DEFINE_IDTENTRY_MCE_USER, DEFINE_FREDENTRY_MCE, and DEFINE_IDTENTRY_RAW), surrounding exc_machine_check_user().
However, exc_machine_check_user() calls irqentry_exit_to_user_mode(), which handles pending thread work and may schedule() if TIF_NEED_RESCHED is set. If the task migrates to another CPU during schedule(), local_db_restore() runs on the new CPU with the dr7 state saved from the old CPU. This corrupts the new CPU's DR7 hardware debug register and leaves the old CPU's DR7 disabled. In short, local_db_save() and local_db_restore() pair must be run on the same CPU.
To fix this, move local_db_save() and local_db_restore() inside exc_machine_check_user() and exc_machine_check_kernel(). In exc_machine_check_user(), DR7 is saved and restored strictly around do_machine_check() to avoid schedule() during migration. In exc_machine_check_kernel(), local_db_save() is called at the entry point to prevent early memory accesses from triggering nested #DB exceptions, and restored on all exits.
--- arch/x86/kernel/cpu/mce/core.c | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-)
arch/x86/kernel/cpu/mce/core.c +10 -17
@@ -2108,6 +2108,9 @@ bool filter_mce(struct mce *m) static __always_inline void exc_machine_check_kernel(struct pt_regs *regs) {irqentry_state_t irq_state; +unsigned long dr7; + + dr7 =local_db_save ();WARN_ON_ONCE (user_mode (regs));
@@ -2116,20 +2119,26 @@ static __always_inline void exc_machine_* mce_check_crashing_cpu() for details. */ if (mca_cfg.initialized &&mce_check_crashing_cpu ()) -return ; + goto out; irq_state =irqentry_nmi_enter (regs);do_machine_check (regs);irqentry_nmi_exit (regs, irq_state); +out: +local_db_restore (dr7); } static __always_inline void exc_machine_check_user(struct pt_regs *regs) { +unsigned long dr7; +irqentry_enter_from_user_mode (regs); + dr7 =local_db_save ();do_machine_check (regs); +local_db_restore (dr7);irqentry_exit_to_user_mode (regs); }
@@ -2138,21 +2147,13 @@ static __always_inline void exc_machine_/* MCE hit kernel mode */ DEFINE_IDTENTRY_MCE (exc_machine_check) { -unsigned long dr7; - - dr7 =local_db_save ();exc_machine_check_kernel (regs); -local_db_restore (dr7); }/* The user mode variant. */ DEFINE_IDTENTRY_MCE_USER (exc_machine_check) { -unsigned long dr7; - - dr7 =local_db_save ();exc_machine_check_user (regs); -local_db_restore (dr7); }#ifdef CONFIG_X86_FRED
@@ -2169,28 +2170,20 @@ DEFINE_IDTENTRY_MCE_USER(exc_machine_che*/ DEFINE_FREDENTRY_MCE (exc_machine_check) { -unsigned long dr7; - - dr7 =local_db_save ();if (user_mode (regs))exc_machine_check_user (regs);else exc_machine_check_kernel (regs); -local_db_restore (dr7); }#endif #else /* 32bit unified entry point */ DEFINE_IDTENTRY_RAW (exc_machine_check) { -unsigned long dr7; - - dr7 =local_db_save ();if (user_mode (regs))exc_machine_check_user (regs);else exc_machine_check_kernel (regs); -local_db_restore (dr7); }#endif
Greg Kroah-Hartman
maintainerPATCH 240/457 x86/PCI: Disable enhanced atomics on AMD NBIO 7.7 and 7.11
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mario Limonciello <[email protected]>
commit 4fde448225123442c5796f54b7a4400e2d3cbaf6 upstream.
Multiple users report data corruption during 64-bit DMA transfers on systems with AMD NBIO 7.7 and 7.11 controllers.
This occurs when BIOS enables AMD "enhanced atomic operations" on PCIe Root Ports. When enhanced atomics are enabled, any 64-bit DMA access may be corrupted.
Disable enhanced atomics using SMN for NBIO 7.7 and 7.11 based models.
--- arch/x86/pci/fixup.c | 99 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+)
arch/x86/pci/fixup.c +99 -0
@@ -886,6 +886,105 @@ static void quirk_clear_strap_no_soft_re } }DECLARE_PCI_FIXUP_FINAL (PCI_VENDOR_ID_AMD ,0x15b8 , quirk_clear_strap_no_soft_reset_dev2_f0); + +/* +* Enhanced atomic operations can cause corruption with 64-bit DMA +* on these devices. +*/ +#define RX_ENH_ATOMIC_EN BIT(8) + +static const u32 nbio_7_7_pcie_smn_addrs[] = { +0x111401d0 , +0x111411d0 , +0x111421d0 , +0x111431d0 , +0x111441d0 , +0x112401d0 , +0x112411d0 , +0x112421d0 , +0x112431d0 , +0x112441d0 , +0x112451d0 , +0x113401d0 , +0x114401d0 , +}; + +static const u32 nbio_7_11_pcie_smn_addrs[] = { +0x112401d0 , +0x112411d0 , +0x112421d0 , +0x112431d0 , +0x112441d0 , +0x112451d0 , +0x113401d0 , +0x113411d0 , +0x113421d0 , +0x113431d0 , +0x113441d0 , +0x113451d0 , +}; + +static void quirk_amd_nbio_enhanced_atomic (struct pci_dev *host_bridge, +const u32 *smn_addrs, +size_t nr_smn_addrs) +{ +bool changed = false; +size_t i; +u32 data; +int ret; + +for (i =0 ; i < nr_smn_addrs; i++) { + ret =amd_smn_read (0 , smn_addrs[i], &data); +if (ret) +continue ; +if (!(data &RX_ENH_ATOMIC_EN )) +continue ; + data = data & ~RX_ENH_ATOMIC_EN ; + ret =amd_smn_write (0 , smn_addrs[i], data); +if (ret) +continue ; +if (changed) +continue ; + ret =amd_smn_read (0 , smn_addrs[i], &data); +if (ret) +continue ; +if (data &RX_ENH_ATOMIC_EN ) +continue ; + changed = true; + } + +if (changed) +pci_info (host_bridge,"enhanced atomics disabled\n" ); +} + +static void quirk_amd_nbio_7_7_disable_enhanced_atomic (struct pci_dev *dev) +{ +quirk_amd_nbio_enhanced_atomic (dev, nbio_7_7_pcie_smn_addrs, +ARRAY_SIZE (nbio_7_7_pcie_smn_addrs)); +} + +static void quirk_amd_nbio_7_11_disable_enhanced_atomic (struct pci_dev *dev) +{ +quirk_amd_nbio_enhanced_atomic (dev, nbio_7_11_pcie_smn_addrs, +ARRAY_SIZE (nbio_7_11_pcie_smn_addrs)); +} + +/* Phoenix, Hawk Point (NBIO 7.7) */ +DECLARE_PCI_FIXUP_FINAL (PCI_VENDOR_ID_AMD ,0x14E8 , + quirk_amd_nbio_7_7_disable_enhanced_atomic); +DECLARE_PCI_FIXUP_RESUME (PCI_VENDOR_ID_AMD ,0x14E8 , + quirk_amd_nbio_7_7_disable_enhanced_atomic); + +/* Strix, Krackan, Strix Halo (NBIO 7.11) */ +DECLARE_PCI_FIXUP_FINAL (PCI_VENDOR_ID_AMD ,0x1507 , + quirk_amd_nbio_7_11_disable_enhanced_atomic); +DECLARE_PCI_FIXUP_RESUME (PCI_VENDOR_ID_AMD ,0x1507 , + quirk_amd_nbio_7_11_disable_enhanced_atomic); +DECLARE_PCI_FIXUP_FINAL (PCI_VENDOR_ID_AMD ,0x1122 , + quirk_amd_nbio_7_11_disable_enhanced_atomic); +DECLARE_PCI_FIXUP_RESUME (PCI_VENDOR_ID_AMD ,0x1122 , + quirk_amd_nbio_7_11_disable_enhanced_atomic); +#endif /*
Greg Kroah-Hartman
maintainerPATCH 241/457 x86/sev: Make vTPM SVSM calls preemption-safe
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Melody Wang <[email protected]>
commit 6c43c72748fffd29dec15cd1f31e9a32949bc437 upstream.
Two functions in the SVSM vTPM guest implementation do not disable preemption when fetching the SVSM Calling Area Address (CAA).
The SVSM CAA is a per-CPU structure. When a thread is preempted and migrated to a different CPU after fetching the per-CPU CAA, the SVSM call will execute on the new CPU with the original CPU's CAA. Which is wrong.
Move the CAA fetching operation inside svsm_perform_call_protocol() which disables interrupts around the SVSM call and thus runs preemption-safe.
--- arch/x86/coco/sev/svsm.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-)
arch/x86/coco/sev/svsm.c +8 -2
@@ -74,6 +74,14 @@ int svsm_perform_call_protocol(struct sv flags =native_local_irq_save (); +/* +* 'caa' is a per-CPU variable. To avoid using a stale or incorrect +* 'caa' if the task is preempted or migrated to another CPU after it +* is fetched, always fetch 'caa' and then issue the SVSM call with +* interrupts disabled. This ensures the correct 'caa' is used. +*/ + call->caa =svsm_get_caa (); + ghcb =__sev_get_ghcb (&state); do {
@@ -321,7 +329,6 @@ int snp_svsm_vtpm_send_command(u8 *buffe {struct svsm_call call = {}; - call.caa =svsm_get_caa (); call.rax =SVSM_VTPM_CALL (SVSM_VTPM_CMD ); call.rcx =__pa (buffer);
@@ -345,7 +352,6 @@ bool snp_svsm_vtpm_probe(void)if (!snp_vmpl)return false; - call.caa =svsm_get_caa (); call.rax =SVSM_VTPM_CALL (SVSM_VTPM_QUERY );if (svsm_perform_call_protocol (&call))
Greg Kroah-Hartman
maintainerPATCH 242/457 vsock: ignore empty child namespace mode writes
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Aldo Ariel Panzardo <[email protected]>
commit 2ec28c09b320ba241bea8a70ee5cb9ccf4a099e8 upstream.
__vsock_net_mode_string() returns success without updating new_mode when the transfer length is zero. Its caller then reads the uninitialized enum and may permanently store a stack-derived value in the write-once child mode.
Return before calling __vsock_net_mode_string() when *lenp is zero so that the helper is never invoked with nothing to parse and new_mode is never read uninitialized. This also prevents an empty write from locking the current mode.
--- net/vmw_vsock/af_vsock.c | 3 +++ 1 file changed, 3 insertions(+)
net/vmw_vsock/af_vsock.c +3 -0
@@ -2873,6 +2873,9 @@ static int vsock_net_child_mode_string(c net = container_of(table->data, struct net, vsock.child_ns_mode); +if (!*lenp) +return 0 ; + ret =__vsock_net_mode_string (table, write, buffer, lenp, ppos,vsock_net_child_mode (net), &new_mode);if (ret)
Greg Kroah-Hartman
maintainerPATCH 243/457 virtio_net: copy zerocopy frags in start_xmit without NAPI
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Willem de Bruijn <[email protected]>
commit 07e1a9408b6c2f9d0cfb757b67dabb52da7a32b2 upstream.
Virtio-net without NAPI frees completed skbs lazily on the next start_xmit. Senders waiting for in-flight zerocopy buffers can deadlock if they cannot transmit more packets, as then no completed packets will be freed.
When !use_napi, virtio-net already calls skb_orphan to avoid waiting up for transmitted skbs to be freed. For zerocopy packets that require deep copying on orphan (i.e. those that do not set SKBFL_DONT_ORPHAN, such as PACKET_TX_RING), call skb_orphan_frags before orphaning to release the buffers.
This fixes the tpacket_snd slot reuse bug on skb_orphan for virtio-net, and prevents PACKET_TX_RING from running out of slots.
This fix also touches vhost_net zerocopy packets, which also do not set SKBFL_DONT_ORPHAN. This is fine: vhost_net packets only encounter virtio-net in nested virtualization, and only if napi_tx is explicitly disabled (it has been default-enabled since Linux 4.12). In that rare case, copying the frags is desirable anyway to prevent holding guest descriptors pinned across unbounded intervals.
This is a prerequisite for the next patch, which converts PACKET_TX_RING to standard zerocopy completion. Without this patch first, a bounded ring sender can stall indefinitely behind a virtio-net virtqueue that cannot reclaim.
--- drivers/net/virtio_net.c | 9 +++++++++ 1 file changed, 9 insertions(+)
drivers/net/virtio_net.c +9 -0
@@ -3349,6 +3349,14 @@ static netdev_tx_t start_xmit(struct sk_else virtqueue_disable_cb (sq->vq); +if (!use_napi && +unlikely (skb_orphan_frags (skb,GFP_ATOMIC ))) { +DEV_STATS_INC (dev, tx_dropped); +dev_kfree_skb_any (skb); + kick = !xmit_more ||netif_xmit_stopped (txq); + goto kick_vq; + } +/* timestamp packet in software */ skb_tx_timestamp (skb);
@@ -3381,6 +3389,7 @@ static netdev_tx_t start_xmit(struct sk_ kick = use_napi ?__netdev_tx_sent_queue (txq, skb->len, xmit_more) : !xmit_more ||netif_xmit_stopped (txq); +kick_vq:if (kick) {if (virtqueue_kick_prepare (sq->vq) &&virtqueue_notify (sq->vq)) {u64_stats_update_begin (&sq->stats.syncp);
Greg Kroah-Hartman
maintainerPATCH 244/457 tipc: reject invalid and unexpected GRP_ACK_MSG to prevent bc_ackers underflow
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
commit 99cc2a62e07a44a22254d7beca9ef1f8ad886d0d upstream.
Commit 48a5fe38772b ("tipc: fix bc_ackers underflow on duplicate GRP_ACK_MSG") rejected duplicate/stale ACKs in tipc_group_proto_rcv() by returning early when less_eq(acked, m->bc_acked).
However, that check remains incomplete in two ways:
1. When grp->bc_ackers is zero (e.g. on a quiet group, when replicast ACKs were not requested, or after all expected members have already acknowledged), an unexpected GRP_ACK_MSG with acked > m->bc_acked passes less_eq() and unconditionally decrements grp->bc_ackers. Because bc_ackers is a u16, this wraps to 65535, causing tipc_group_bc_cong() to permanently report congestion and blocking all future group broadcasts on the socket.
2. During an active broadcast round (grp->bc_ackers > 0), the sender transmits packet S and advances grp->bc_snd_nxt to S + 1. Receivers increment their expected counter to S + 1 upon consuming packet S, so the only valid ACK value for the current round is strictly acked == grp->bc_snd_nxt.
However, tipc_group_update_bc_members() initializes each member's m->bc_acked to prev = grp->bc_snd_nxt - 1 (S - 1 before increment). This leaves a 2-sequence gap (S - 1 to S + 1) in sequence space. An incoming ACK is therefore neither rejected as duplicate nor prevented from decrementing grp->bc_ackers if an unexpected or stale value (such as S) is received. A member sending acked = S followed by acked = S + 1 could decrement grp->bc_ackers twice in the same round, prematurely clearing bc_ackers or underflowing it.
Fix this by:
- Dropping GRP_ACK_MSG immediately if grp->bc_ackers is zero.
- Requiring acked == grp->bc_snd_nxt and rejecting duplicates where m->bc_acked == acked. Because replicast broadcast rounds are strictly sequential, only grp->bc_snd_nxt can be acknowledged, and each member can acknowledge at most once per round.
Note that a related pre-existing issue in tipc_group_delete_member() (where grp->bc_ackers decrementing to zero upon member departure does not restore *grp->open or trigger a socket wakeup) will be addressed in a separate patch.
--- net/tipc/group.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
net/tipc/group.c +2 -2
@@ -797,10 +797,10 @@ void tipc_group_proto_rcv(struct tipc_grtipc_group_open (m, usr_wakeup);return ;case GRP_ACK_MSG : -if (!m) +if (!m || !grp->bc_ackers)return ; acked =msg_grp_bc_acked (hdr); -if (less_eq (acked, m->bc_acked)) +if (acked != grp->bc_snd_nxt || m->bc_acked == acked)return ; m->bc_acked = acked;if (--grp->bc_ackers)
Greg Kroah-Hartman
maintainerPATCH 245/457 tcp: prevent collapsing skbs across boundary in rtx queue
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Willem de Bruijn <[email protected]>
commit fc6d80eb504458d6416b75a94188b268c95c6533 upstream.
tcp_write_collapse_fence() sets TCP_SKB_CB(skb)->eor = 1 on tcp_write_queue_tail(sk) to prevent skbs queued after a switch to device encryption from being collapsed into earlier skbs.
The fence is a no-op if all earlier data has already been transmitted when the switch happens: sk->sk_write_queue is empty. The not yet acknowledged earlier skbs wait in sk->tcp_rtx_queue with eor 0.
On a subsequent retransmit or SACK shift, tcp_retrans_try_collapse() or tcp_shift_skb_data() can then merge an skb queued after the switch into one queued before it.
Both users of the fence are affected:
- psp: devices only encrypt skbs with skb->decrypted set. The merged skb keeps decrypted = 0 from the earlier skb, so merged data sent after psp_sock_assoc_set_tx() is retransmitted in cleartext.
- tls device offload: the merged skb straddles the start marker set in tls_set_device_offload(). The software fallback (fill_sg_in() returns -EINVAL) and the mlx5, nfp and funeth drivers cannot handle such an skb and drop it. Every retransmit rebuilds the same skb, so the connection stalls.
Fix this in two places, for defense in depth:
1. Fall back to tcp_rtx_queue_tail(sk) in tcp_write_collapse_fence() when tcp_write_queue_tail(sk) is NULL.
2. Check !skb_cmp_decrypted(to, from) in tcp_skb_can_collapse(), as tcp_skb_can_collapse_rx() does on receive. skb_shift(), which both collapse paths call, already has a DEBUG_NET_WARN_ON_ONCE() for this condition.
--- include/net/tcp.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
include/net/tcp.h +2 -2
@@ -1232,9 +1232,9 @@ static inline bool tcp_skb_can_collapse_static inline bool tcp_skb_can_collapse (const struct sk_buff *to,const struct sk_buff *from) { -/* skb_cmp_decrypted() not needed, use tcp_write_collapse_fence() */ return likely (tcp_skb_can_collapse_to (to) &&mptcp_skb_can_collapse (to, from) && + !skb_cmp_decrypted (to, from) &&skb_pure_zcopy_same (to, from) &&skb_frags_readable (to) ==skb_frags_readable (from)); }
@@ -2327,7 +2327,7 @@ static inline void tcp_rtx_queue_unlink_static inline void tcp_write_collapse_fence (struct sock *sk) { -struct sk_buff *skb =tcp_write_queue_tail (sk); +struct sk_buff *skb =tcp_write_queue_tail (sk) ?:tcp_rtx_queue_tail (sk);if (skb)TCP_SKB_CB (skb)->eor =1 ;
Greg Kroah-Hartman
maintainerPATCH 246/457 sctp: discard the rest of the packet on a stale-cookie error
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Aohan Mei <[email protected]>
commit 4498467a8af06cfa3d71cb04bd7c4170dec8f449 upstream.
When an association is in COOKIE-ECHOED state and the peer sends a bundled [ERROR(Stale Cookie)][DATA] packet from one of its non-primary addresses, processing the ERROR chunk takes the non-fatal stale-cookie retry path sctp_sf_do_5_2_6_stale(), which queues SCTP_CMD_DEL_NON_PRIMARY while keeping the association alive. sctp_cmd_del_non_primary() removes every non-primary transport - including the very transport this packet arrived on, which is still referenced by the receive lookup and shared by all chunks of the packet via chunk->transport.
sctp_assoc_rm_peer() does redirect asoc->peer.last_data_from away from the removed transport, but right afterwards the bundled DATA chunk makes sctp_assoc_bh_rcv() re-register asoc->peer.last_data_from = chunk->transport unconditionally, undoing the redirection with the just-removed transport.
Once the packet is done, the receive reference is dropped and the transport is RCU-freed, while the surviving association keeps the dangling last_data_from. A later FWD-TSN (or the delayed SACK timer) makes sctp_gen_sack() dereference it (->param_flags and friends), and sctp_make_sack()/sctp_outq_select_transport() may write to the freed object and link it into the live transport list. This is a use-after-free triggerable by any malicious SCTP peer (or a local unprivileged user acting as one) with no capabilities required:
BUG: KASAN: slab-use-after-free in sctp_do_sm+0x498a/0x5660
Read of size 4 at addr ffff88800e1e356c by task poc/115
Call Trace: sctp_do_sm <- sctp_assoc_bh_rcv <- sctp_inq_push <-
sctp_rcv <- ip_protocol_deliver_rcu <- ip_rcv
Allocated: sctp_transport_new <- sctp_assoc_add_peer <-
sctp_process_init (INIT-ACK processing)
Freed: kfree <- sctp_transport_destroy_rcu <- rcu_core
(call_rcu queued by sctp_transport_put at end of sctp_rcv)
The buggy address is located 364 bytes inside of freed 1024-byte
region [ffff88800e1e3400, ffff88800e1e3800), cache kmalloc-1kNote that commit 03a9d10ecf71 ("sctp: drop a chunk if its transport was removed") only covers the window between the receive lookup and the chunk processing (e.g. an ASCONF DEL-IP racing the socket backlog); here the transport is removed *while* the packet is being processed, by an earlier chunk of the same packet, so the drop in sctp_inq_push() does not reach this path. Verified with the bundled [ERROR(Stale Cookie)][DATA] + FWD-TSN reproducer: the KASAN report above still fires with that commit applied, and is gone with this patch on top.
Fix it by discarding the rest of the packet on this path, as suggested by Xin. After the stale-cookie ERROR has sent the association back to COOKIE-WAIT and removed the non-primary transports, the remaining chunks of the packet can only run against the restarted handshake while referencing the removed arrival transport through chunk->transport: besides the last_data_from registration above, sctp_cmd_setup_t2() and the sctp_make_*() reply builders would also copy that pointer into association-lifetime state that sctp_assoc_rm_peer() has already sanitized. Let the peer retransmit them, in line with what sctp_inq_push() does for chunks whose transport was removed before processing.
--- net/sctp/sm_statefuns.c | 2 ++ 1 file changed, 2 insertions(+)
net/sctp/sm_statefuns.c +2 -0
@@ -2654,6 +2654,8 @@ static enum sctp_disposition sctp_sf_do_sctp_add_cmd_sf (commands,SCTP_CMD_REPLY ,SCTP_CHUNK (reply)); +sctp_add_cmd_sf (commands,SCTP_CMD_DISCARD_PACKET ,SCTP_NULL ()); +return SCTP_DISPOSITION_CONSUME ; nomem:
Greg Kroah-Hartman
maintainerPATCH 247/457 af_packet: fix integer overflow in prb_calc_retire_blk_tmo()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Dairui Zhang <[email protected]>
commit 56d82862a0a243ac14ba11b6d7b57ddc2d064b95 upstream.
prb_calc_retire_blk_tmo() computes in 32-bit int arithmetic:
mbits = (blk_size_in_bytes * 8) / (1024 * 1024);
If I'm reading the validation right, tp_block_size is user controlled and packet_set_ring() only rejects values that are <= 0 as int or not page aligned, so a 256MiB block goes right through (and alloc_one_pg_vec_page() even has a vzalloc fallback for it). 0x10000000 * 8 wraps to INT_MIN, and on a NIC reporting 1 Gbps (div == 1) the function ends up returning -2047.
The condition is actually (8 * size) mod 2^32 >= 2^31 && div == 1, so the trigger set is [256,512), [768,1024), [1280,1536) and [1792,2048) MiB. Other sizes wrap to non-negative values and faster links divide the unsigned value back below 2^31, which is why this doesn't blow up for everyone.
What makes it fatal is what happens next in init_prb_bdqc():
p1->interval_ktime = ms_to_ktime(prb_calc_retire_blk_tmo(...));
hrtimer_start(&p1->retire_blk_timer, p1->interval_ktime,
HRTIMER_MODE_REL_SOFT);A negative relative timeout expires immediately. The callback unconditionally returns HRTIMER_RESTART, and hrtimer_forward() turns the negative interval into hrtimer_resolution:
if (interval < hrtimer_resolution)
interval = hrtimer_resolution;
So the SOFT timer re-fires at the maximum rate forever, holding sk_receive_queue.lock each pass. One CPU spins in softirq until the socket is closed. Repeat with more rings and the machine is gone.
The overflow itself is ancient - it was introduced together with TPACKET_V3 in f6fb8f100b80 ("af-packet: TPACKET_V3 flexible buffer implementation."). Its effect prior to f7460d2989fa ("net:af_packet: Use hrtimer to do the retire operation", v6.18) was not as clear-cut, though: the return value was stored into an unsigned short retire_blk_tov, so a negative result was truncated, and a 0-jiffy delay loop could be programmed as well. Neither is nearly as detrimental as the immediate maximum-rate spin the hrtimer conversion turned it into.
(Unrelated to CVE-2019-20812 - that one was the ethtool failure path returning 0, which now returns DEFAULT_PRB_RETIRE_TOV.)
Reproducer, needs CAP_NET_RAW (a --network host container has it by default) and a 1 Gbps NIC (QEMU e1000 works):
int fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
bind(fd, ...);
int v = TPACKET_V3;
setsockopt(fd, SOL_PACKET, PACKET_VERSION, &v, sizeof(v));
struct tpacket_req3 req = {
.tp_block_size = 0x10000000,
.tp_block_nr = 1,
.tp_frame_size = 2048,
.tp_frame_nr = 0x10000000 / 2048,
.tp_retire_blk_tov = 0,
};
setsockopt(fd, SOL_PACKET, PACKET_RX_RING, &req, sizeof(req));Compute in 64 bits instead. The operands are already bounded by the existing validation, so nothing else changes. If you'd prefer a different fix, just say so and I'll respin.
--- net/packet/af_packet.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/packet/af_packet.c +1 -1
@@ -617,7 +617,7 @@ static int prb_calc_retire_blk_tmo(strucreturn DEFAULT_PRB_RETIRE_TOV ; div = ecmd.base.speed /1000 ; - mbits = (blk_size_in_bytes *8 ) / (1024 *1024 ); + mbits = (u64 )blk_size_in_bytes *8 / (1024 *1024 );if (div) mbits /= div;
Greg Kroah-Hartman
maintainerPATCH 248/457 arm64/boot: Disable trapping of PMZR_EL0 writes to EL2
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Fuad Tabba <[email protected]>
commit 2bc6b218717b9d08f466f88209251d54bc09b207 upstream.
__init_el2_fgt2() writes one mask to both HDFGRTR2_EL2 and HDFGWTR2_EL2. PMZR_EL0 is write-only, so its trap bit, nPMZR_EL0, exists only in HDFGWTR2_EL2 and is therefore never set: a PMZR_EL0 write from the host traps to EL2, where the nVHE hypervisor has no handler and BUG()s. The kernel never writes PMZR_EL0, but kernel.perf_user_access=1 has the PMU driver set PMUSERENR_EL0.UEN for a task with a user-read event, so a write from EL0 reaches the trap and takes the host down without a panic message.
Accumulate the HDFGWTR2_EL2 bits separately, as __init_el2_fgt() already does for HDFGWTR_EL2, and set nPMZR_EL0 with the other FEAT_PMUv3p9 bits.
--- Documentation/arch/arm64/booting.rst | 1 + arch/arm64/include/asm/el2_setup.h | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-)
Documentation/arch/arm64/booting.rst +1 -0
@@ -465,6 +465,7 @@ Before jumping into the kernel, the foll - HDFGWTR2_EL2.nPMICNTR_EL0 (bit 2) must be initialised to 0b1. - HDFGWTR2_EL2.nPMICFILTR_EL0 (bit 3) must be initialised to 0b1. - HDFGWTR2_EL2.nPMUACR_EL1 (bit 4) must be initialised to 0b1. + - HDFGWTR2_EL2.nPMZR_EL0 (bit 21) must be initialised to 0b1. For CPUs with SPE data source filtering (FEAT_SPE_FDS):
arch/arm64/include/asm/el2_setup.h +8 -1
@@ -418,6 +418,7 @@ b.lt .Lskip_fgt2_\@mov x0, xzr + mov x2, xzr mrs x1, id_aa64dfr0_el1 ubfx x1, x1, #ID_AA64DFR0_EL1_PMUVer_SHIFT, #4 cmp x1, #ID_AA64DFR0_EL1_PMUVer_V3P9
@@ -426,6 +427,11 @@ orr x0, x0, #HDFGRTR2_EL2_nPMICNTR_EL0 orr x0, x0, #HDFGRTR2_EL2_nPMICFILTR_EL0 orr x0, x0, #HDFGRTR2_EL2_nPMUACR_EL1 + orr x2, x2, #HDFGWTR2_EL2_nPMICNTR_EL0 + orr x2, x2, #HDFGWTR2_EL2_nPMICFILTR_EL0 + orr x2, x2, #HDFGWTR2_EL2_nPMUACR_EL1 +/* PMZR_EL0 is write-only, so it has no read trap to disable */ + orr x2, x2, #HDFGWTR2_EL2_nPMZR_EL0 .Lskip_pmuv3p9_\@:/* If SPE is implemented, */ __spe_vers_imp .Lskip_spefds_\@, ID_AA64DFR0_EL1_PMSVer_IMP, x1
@@ -436,10 +442,11 @@ cbz x1, .Lskip_spefds_\@/* disable traps of PMSDSFR to EL2. */ orr x0, x0, #HDFGRTR2_EL2_nPMSDSFR_EL1 + orr x2, x2, #HDFGWTR2_EL2_nPMSDSFR_EL1 .Lskip_spefds_\@: msr_s SYS_HDFGRTR2_EL2, x0 - msr_s SYS_HDFGWTR2_EL2, x0 + msr_s SYS_HDFGWTR2_EL2, x2 msr_s SYS_HFGRTR2_EL2, xzr msr_s SYS_HFGWTR2_EL2, xzr msr_s SYS_HFGITR2_EL2, xzr
Greg Kroah-Hartman
maintainerPATCH 249/457 arm64: errata: match the target implementation CPUs own MIDR
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Carlier <[email protected]>
commit b7403afb7a5f85073243df10238b3483958ad69e upstream.
__is_affected_midr_range() is handed the MIDR and REVIDR of one target implementation CPU, but tests the erratum's range with is_midr_in_range(), which re-scans all of target_impl_cpus[] and ignores the @midr argument. The range test is thus constant across the per-CPU loop in is_affected_midr_range() and only answers "is any target CPU in range".
Since just the fixed_revs REVIDR check uses the iteration's own registers, an out-of-range target CPU can decide whether a MIDR_FIXED() exemption applies. A VM then enables a workaround whose only in-range CPU is fixed silicon, e.g. erratum 2658417 on a Cortex-A510 r1p1 with REVIDR_EL1[25] set.
Factor the range test into __is_midr_in_range(), which takes an explicit MIDR, and use it in __is_affected_midr_range().
--- arch/arm64/kernel/cpu_errata.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-)
arch/arm64/kernel/cpu_errata.c +9 -6
@@ -28,18 +28,21 @@ bool cpu_errata_set_target_impl(u64 num,return true; } +static inline bool __is_midr_in_range (u32 midr,struct midr_range const *range) +{ +return midr_is_cpu_model_range (midr, range->model, + range->rv_min, range->rv_max); +} +static inline bool is_midr_in_range (struct midr_range const *range) {int i;if (!target_impl_cpu_num) -return midr_is_cpu_model_range (read_cpuid_id (), range->model, - range->rv_min, range->rv_max); +return __is_midr_in_range (read_cpuid_id (), range);for (i =0 ; i < target_impl_cpu_num; i++) { -if (midr_is_cpu_model_range (target_impl_cpus[i].midr, - range->model, - range->rv_min, range->rv_max)) +if (__is_midr_in_range (target_impl_cpus[i].midr, range))return true; }return false;
@@ -59,7 +62,7 @@ __is_affected_midr_range(const struct ar u32 midr, u32 revidr) {const struct arm64_midr_revidr *fix; -if (!is_midr_in_range (&entry->midr_range)) +if (!__is_midr_in_range (midr, &entry->midr_range))return false; midr &=MIDR_REVISION_MASK |MIDR_VARIANT_MASK ;
Greg Kroah-Hartman
maintainerPATCH 250/457 ata: libata-scsi: bound the ATA passthru sense descriptor writes
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Matthias Goergens <[email protected]>
commit 80320b278fea07ffcda3f57b67b61658e0a4e1ca upstream.
When an ATA PASS-THROUGH command to an ATAPI device fails, the sense buffer holds the device's REQUEST SENSE reply, and ata_scsi_set_passthru_sense_fields() trusts its additional length byte, sb[7], when adding the ATA Status Return descriptor. A faulty or malicious device can use that to make the kernel read and write past the 96-byte buffer in three ways:
- scsi_sense_desc_find() is passed sb[7] + 8 as the buffer length, so its clamp against sb[7] does nothing and the walk runs off the end.
- A type-9 descriptor found near the end is filled in unchecked.
- A new descriptor at sb[8 + len] needs len + 22 bytes, not len + 14, so len 75..82 writes up to 8 bytes past the end.
Reproduced with KASAN under qemu, with the emulated ATAPI REQUEST SENSE reply patched:
BUG: KASAN: slab-out-of-bounds in scsi_sense_desc_find+0x1a5/0x210 BUG: KASAN: slab-out-of-bounds in ata_scsi_qc_complete+0x1a15/0x1a50
Both are gone with this patch, and a valid descriptor is still filled in.
--- drivers/ata/libata-scsi.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-)
drivers/ata/libata-scsi.c +8 -2
@@ -261,12 +261,18 @@ static void ata_scsi_set_passthru_sense_/* descriptor format */ len = sb[7 ]; - desc = (char *)scsi_sense_desc_find (sb, len +8 ,9 ); + desc = (char *)scsi_sense_desc_find (sb,SCSI_SENSE_BUFFERSIZE ,9 );if (!desc) { -if (SCSI_SENSE_BUFFERSIZE < len +14 ) +/* +* The descriptor is written at sb[8 + len] and is 14 +* bytes long, so it needs len + 22 bytes of buffer. +*/ +if (len +22 >SCSI_SENSE_BUFFERSIZE )return ; sb[7 ] = len +14 ; desc = sb +8 + len; + }else if (desc - sb >SCSI_SENSE_BUFFERSIZE -14 ) { +return ; } desc[0 ] =9 ; desc[1 ] =12 ;
Greg Kroah-Hartman
maintainerPATCH 251/457 bna: prevent IOC timer rearm during teardown
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Myeonghun Pak <[email protected]>
commit 77b1718e39e5c9f6956fb60807326af20baf889d upstream.
bna: prevent IOC timer rearm during teardown
bnad_pci_remove() and the probe disable_ioceth path call timer_delete_sync() for ioc_timer, sem_timer and hb_timer, but not for iocpf_timer. bnad_iocpf_timeout() then takes bnad->bna_lock after free_netdev() has freed the struct bnad.
Deleting iocpf_timer last does not fix this. sem_timer and iocpf_timer rearm each other: bnad_iocpf_sem_timeout() can arm iocpf_timer, and bnad_iocpf_timeout() arms sem_timer from bfa_ioc_hw_sem_get() when the semaphore is busy. timer_delete_sync() only waits out its own callback. bnad_ioceth_disable() can time out and leave that callback live.
Shut all four IOC timers down with timer_shutdown_sync() on both paths, so a later mod_timer() is ignored.
This issue was identified during our ongoing static-analysis research while reviewing kernel code.
--- drivers/net/ethernet/brocade/bna/bnad.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-)
drivers/net/ethernet/brocade/bna/bnad.c +18 -6
@@ -2571,6 +2571,22 @@ bnad_ioceth_disable(struct bnad *bnad)return err; } +/* +* The IOC timers rearm one another, so deleting one cannot stop a +* sibling callback from arming it again. Shut them down so a later +* mod_timer() is ignored. +*/ +static void +bnad_ioc_timers_shutdown (struct bnad *bnad) +{ +struct bfa_ioc *ioc = &bnad->bna.ioceth.ioc; + +timer_shutdown_sync (&ioc->ioc_timer); +timer_shutdown_sync (&ioc->sem_timer); +timer_shutdown_sync (&ioc->hb_timer); +timer_shutdown_sync (&ioc->iocpf_timer); +} +static int bnad_ioceth_enable (struct bnad *bnad) {
@@ -3727,9 +3743,7 @@ probe_uninit:bnad_res_free (bnad, &bnad->mod_res_info[0 ],BNA_MOD_RES_T_MAX ); disable_ioceth:bnad_ioceth_disable (bnad); -timer_delete_sync (&bnad->bna.ioceth.ioc.ioc_timer); -timer_delete_sync (&bnad->bna.ioceth.ioc.sem_timer); -timer_delete_sync (&bnad->bna.ioceth.ioc.hb_timer); +bnad_ioc_timers_shutdown (bnad);spin_lock_irqsave (&bnad->bna_lock, flags);bna_uninit (bna);spin_unlock_irqrestore (&bnad->bna_lock, flags);
@@ -3770,9 +3784,7 @@ bnad_pci_remove(struct pci_dev *pdev)mutex_lock (&bnad->conf_mutex);bnad_ioceth_disable (bnad); -timer_delete_sync (&bnad->bna.ioceth.ioc.ioc_timer); -timer_delete_sync (&bnad->bna.ioceth.ioc.sem_timer); -timer_delete_sync (&bnad->bna.ioceth.ioc.hb_timer); +bnad_ioc_timers_shutdown (bnad);spin_lock_irqsave (&bnad->bna_lock, flags);bna_uninit (bna);spin_unlock_irqrestore (&bnad->bna_lock, flags);
Greg Kroah-Hartman
maintainerPATCH 252/457 bpf: fs/xattr: dont assume the inode is locked in path_unlink/path_rmdir
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Andrea Parri <[email protected]>
commit 35d442ed1f86465e49df3119fb898f985186db13 upstream.
bpf_lsm_has_d_inode_locked() makes the verifier rewrite bpf_[set|remove]_dentry_xattr() to the _locked variants, which assume that the caller already holds the inode's i_rwsem. The path_unlink and path_rmdir hooks are listed, but security_path_unlink() and security_path_rmdir() run before vfs_unlink()/vfs_rmdir() take the victim inode's i_rwsem, so a sleepable BPF LSM program attached to either hook mutates the victim's xattrs without the lock held.
Drop the two path hooks from d_inode_locked_hooks so that the verifier keeps the locking bpf_[set|remove]_dentry_xattr() variants, which take the lock themselves.
--- fs/bpf_fs_kfuncs.c | 4 ---- 1 file changed, 4 deletions(-)
fs/bpf_fs_kfuncs.c +0 -4
@@ -419,10 +419,6 @@ BTF_ID(func, bpf_lsm_inode_rmdir) BTF_ID(func, bpf_lsm_inode_setattr) BTF_ID(func, bpf_lsm_inode_setxattr) BTF_ID(func, bpf_lsm_inode_unlink) -#ifdef CONFIG_SECURITY_PATH -BTF_ID(func, bpf_lsm_path_unlink) -BTF_ID(func, bpf_lsm_path_rmdir) -#endif /* CONFIG_SECURITY_PATH */ BTF_SET_END (d_inode_locked_hooks) bool bpf_lsm_has_d_inode_locked(const struct bpf_prog *prog)
Greg Kroah-Hartman
maintainerPATCH 253/457 crypto: s390/hmac - Generate intermediate CV for API partial block handling
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Holger Dengler <[email protected]>
commit 10396a2d6d41d594975b6ece712278570c3c970c upstream.
The API partial block handling requires a intermediate chaining value (CV). The internal function hash_data() sets the function code correctly, so also call cpacf_kimd() instruction for intermediate CV generation, as cpacf_klmd() always generate the final hash value.
--- arch/s390/crypto/hmac_s390.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
arch/s390/crypto/hmac_s390.c +4 -1
@@ -150,7 +150,10 @@ static int hash_data(const u8 *in, unsig#undef PARAM_INIT -cpacf_klmd (func, ¶m, in, inlen); +if (final) +cpacf_klmd (func, ¶m, in, inlen); +else +cpacf_kimd (func, ¶m, in, inlen);memcpy (digest, ¶m, digestsize);
Greg Kroah-Hartman
maintainerPATCH 254/457 cgroup/cpuset: Return PERR_NOCPUS in remote_partition_enable() on subpartitions_cpus conflict
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hui Peng <[email protected]>
commit 31c88350b7dd1522792f726f79607f31bb55c50f upstream.
When a remote partition is created underneath an existing local partition via a non-partition (PRS_MEMBER) intermediate cgroup, update_prstate() sees parent->partition_root_state == PRS_MEMBER and calls remote_partition_enable().
Commit 86888c7bd117 ("cgroup/cpuset: Add warnings to catch inconsistency in exclusive CPUs") replaced the cpumask_intersects(tmp->new_cpus, subpartitions_cpus) error check in remote_partition_enable() with WARN_ON_ONCE(). As a result, remote_partition_enable() emits a warning and proceeds to enable the remote partition on CPUs that are already owned by the ancestor local partition in subpartitions_cpus.
This can be reproduced on Linux 7.3.0-rc3 with:
mkdir -p /tmp/cg1
mount -t cgroup2 none /tmp/cg1
echo "+cpuset" > /tmp/cg1/cgroup.subtree_control
mkdir /tmp/cg1/A
echo 1 > /tmp/cg1/A/cpuset.cpus
echo 1 > /tmp/cg1/A/cpuset.cpus.exclusive echo root > /tmp/cg1/A/cpuset.cpus.partition echo "+cpuset" > /tmp/cg1/A/cgroup.subtree_control
mkdir /tmp/cg1/A/B
echo 1 > /tmp/cg1/A/B/cpuset.cpus
echo 1 > /tmp/cg1/A/B/cpuset.cpus.exclusive echo "+cpuset" > /tmp/cg1/A/B/cgroup.subtree_control
mkdir /tmp/cg1/A/B/D
echo 1 > /tmp/cg1/A/B/D/cpuset.cpus
echo 1 > /tmp/cg1/A/B/D/cpuset.cpus.exclusive echo root > /tmp/cg1/A/B/D/cpuset.cpus.partition
which triggers:
WARNING: kernel/cgroup/cpuset.c:1594 at remote_partition_enable+0x1c1/0x300
and leaves both /tmp/cg1/A and /tmp/cg1/A/B/D as active root partitions claiming exclusive CPU 1.
Fix this by returning PERR_NOCPUS when tmp->new_cpus intersects subpartitions_cpus in remote_partition_enable(), matching the error code used by remote_cpus_update() for the same subpartitions_cpus conflict, and add a regression test case to tools/testing/selftests/cgroup/test_cpuset_prs.sh.
Tested in QEMU on Linux 7.3.0-rc3 using the reproducer above and tools/testing/selftests/cgroup/test_cpuset_prs.sh.
--- kernel/cgroup/cpuset.c | 3 ++- tools/testing/selftests/cgroup/test_cpuset_prs.sh | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-)
kernel/cgroup/cpuset.c +2 -1
@@ -1552,10 +1552,11 @@ static int remote_partition_enable(struc* above it or remote partition root underneath it is not allowed. */ compute_excpus (cs, tmp->new_cpus); -WARN_ON_ONCE (cpumask_intersects (tmp->new_cpus, subpartitions_cpus));if (!cpumask_intersects (tmp->new_cpus, cpu_active_mask) ||cpumask_subset (top_cpuset.effective_cpus, tmp->new_cpus))return PERR_INVCPUS ; +if (cpumask_intersects (tmp->new_cpus, subpartitions_cpus)) +return PERR_NOCPUS ;if (((new_prs ==PRS_ISOLATED ) && !isolated_cpus_can_update (tmp->new_cpus,NULL )) ||prstate_housekeeping_conflict (new_prs, tmp->new_cpus))
tools/testing/selftests/cgroup/test_cpuset_prs.sh +2 -0
@@ -298,6 +298,8 @@ TEST_MATRIX=( " C0-4:X2-4 C1-4:X2-4:P2 C2-4:X4:P1 \ . . . X1 . 0 A1:0-1|A2:2-4|A3:2-4 \ A1:P0|A2:P2|A3:P-1 2-4" + " CX1-3:P1 CX1-3 CX1-3 . . . P1 . 0 A1:1-3|A2:1-3|A3:1-3 \ + A1:P1|A2:P0|A3:P-1" # Remote partition offline tests " C0-3 C1-3 C2-3 . X2-3 X2-3 X2-3:P2:O2=0 . 0 A1:0-1|A2:1|A3:3 A1:P0|A3:P2 2-3"
Greg Kroah-Hartman
maintainerPATCH 255/457 cgroup/pids: Restore pids.events notifications in local mode
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Guopeng Zhang <[email protected]>
commit 1765a153d985c231357145e26798f9408db10e42 upstream.
A fork rejected by the pids controller increments the counter reported by pids.events. When local event accounting is selected, however, pids_event() returns after notifying only events_local_file, leaving pids.events pollers asleep.
On legacy hierarchies, pids.events.local does not exist. With pids_localevents, pids.events reports the same local counter. In both cases, pids.events changes without generating a notification.
This can be reproduced with a pids_localevents mount:
mkdir /tmp/test
mount -t cgroup2 -o pids_localevents none /tmp/test mkdir /tmp/test/t
echo 1 > /tmp/test/t/pids.max
cat /tmp/test/t/pids.events # max 0
timeout 3 inotifywait -e modify /tmp/test/t/pids.events & sh -c 'echo $$ > /tmp/test/t/cgroup.procs; (true &)' 2>/dev/null wait
cat /tmp/test/t/pids.events # max 1
Without this patch, inotifywait times out without reporting an event. Notify pids.events before returning from the local event path.
--- kernel/cgroup/pids.c | 5 +++++ 1 file changed, 5 insertions(+)
kernel/cgroup/pids.c +5 -0
@@ -253,6 +253,11 @@ static void pids_event(struct pids_cgrou }if (!cgroup_subsys_on_dfl (pids_cgrp_subsys) || cgrp_dfl_root.flags &CGRP_ROOT_PIDS_LOCAL_EVENTS ) { +/* +* pids.events reports the local counter on legacy hierarchies +* and when pids_localevents is enabled. +*/ +cgroup_file_notify (&p->events_file);cgroup_file_notify (&p->events_local_file);return ; }
Greg Kroah-Hartman
maintainerPATCH 256/457 fou: reject omitted FOU_ATTR_IPPROTO on FOU_ENCAP_DIRECT
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hui Peng <[email protected]>
commit d22609f3d13fc5baacd92c222731b03c593401db upstream.
Commit 7a9bc9e3f423 ("fou: Don't allow 0 for FOU_ATTR_IPPROTO.") added NLA_POLICY_MIN(NLA_U8, 1) to fou_nl_policy[FOU_ATTR_IPPROTO], which rejects an explicitly supplied FOU_ATTR_IPPROTO == 0 attribute with -ERANGE.
However, FOU_ATTR_IPPROTO is an optional netlink attribute. When a user sends FOU_CMD_ADD with FOU_ATTR_TYPE set to FOU_ENCAP_DIRECT and omits FOU_ATTR_IPPROTO entirely, nla_policy validation succeeds and parse_nl_config() leaves cfg->protocol as 0 (from memset(cfg, 0, sizeof(*cfg))). fou_create() then creates a FOU_ENCAP_DIRECT socket with fou->protocol == 0.
In fou_udp_recv(), returning -fou->protocol to udp_queue_rcv_one_skb() triggers IP protocol resubmission when fou->protocol > 0, whereas returning 0 tells the UDP tunnel layer that the skb was consumed without freeing it. When fou->protocol == 0, every packet received on the socket returns 0 from fou_udp_recv() and leaks the sk_buff.
Reject FOU_ENCAP_DIRECT when !cfg->protocol in fou_create() so that creating a direct encapsulation port without FOU_ATTR_IPPROTO fails with -EINVAL while leaving FOU_CMD_DEL and FOU_CMD_GET (which share parse_nl_config()) unaffected.
Tested in QEMU against Linux 7.3.0-rc3 by sending a FOU_CMD_ADD Generic Netlink request with FOU_ATTR_PORT = 5555 and FOU_ATTR_TYPE = FOU_ENCAP_DIRECT while omitting FOU_ATTR_IPPROTO. On the unfixed kernel, FOU_CMD_ADD succeeds (err = 0), FOU_CMD_GET reports fou->type = 1 and fou->protocol = 0, and sending 4000 UDP packets to 127.0.0.1:5555 leaks all 4000 sk_buffs (SUnreclaim in /proc/meminfo grows from 41456 kB to 59008 kB, +17552 kB); with this patch applied, FOU_CMD_ADD is rejected with -EINVAL (-22).
--- net/ipv4/fou_core.c | 4 ++++ 1 file changed, 4 insertions(+)
net/ipv4/fou_core.c +4 -0
@@ -600,6 +600,10 @@ static int fou_create(struct net *net, s/* Initial for fou type */ switch (cfg->type) {case FOU_ENCAP_DIRECT : +if (!cfg->protocol) { + err = -EINVAL ; + goto error; + } tunnel_cfg.encap_rcv = fou_udp_recv; tunnel_cfg.gro_receive = fou_gro_receive; tunnel_cfg.gro_complete = fou_gro_complete;
Greg Kroah-Hartman
maintainerPATCH 257/457 fprobe: Terminate the fgraph_data list when the reservation is not filled
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Carlier <[email protected]>
commit 1d653a183973f5283a3db5a38cd5e195eb152244 upstream.
fprobe_fgraph_entry() reserves shadow stack space for every fprobe with an exit handler, but only fills it for those whose entry handler returns
0. fgraph_reserve_data() does not clear the area, so fprobe_return() parses the unused tail as headers left over from an earlier call, and an exit handler can run twice or despite its entry handler asking to skip it.
Write a zero word after the last entry to terminate the walk. A zeroed slot does not decode to a NULL fprobe on the arches that encode the header into one unsigned long, since arch_decode_fprobe_header_fp() ORs in FPROBE_HEADER_MSB_PATTERN, so make read_fprobe_header() return NULL for a zeroed slot.
--- kernel/trace/fprobe.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+)
kernel/trace/fprobe.c +15 -0
@@ -171,6 +171,11 @@ static inline bool write_fprobe_header(ustatic inline void read_fprobe_header (unsigned long *stack,struct fprobe **fp,unsigned int *size_words) { +if (!*stack) { + *fp =NULL ; + *size_words =0 ; +return ; + } *fp =arch_decode_fprobe_header_fp (*stack); *size_words =arch_decode_fprobe_header_size (*stack); }
@@ -203,6 +208,12 @@ static inline void read_fprobe_header(un {struct __fprobe_header *fph = (struct __fprobe_header *)stack; +if (!*stack) { + *fp =NULL ; + *size_words =0 ; +return ; + } + *fp = fph->fp; *size_words = fph->size_words; }
@@ -642,6 +653,10 @@ static int fprobe_fgraph_entry(struct ft } } +/* Terminate the list, fgraph_reserve_data() does not clear it. */ +if (used && used < reserved_words) + fgraph_data[used] =0 ; +/* If any exit_handler is set, data must be used. */ return used !=0 ; }
Greg Kroah-Hartman
maintainerPATCH 258/457 fs/ntfs3: use d_instantiate_new() in ntfs_create_inode() and murder syzbots "WARNING in do_new_mount" saga
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christian Brauner <[email protected]>
commit 1abd643f3783ea8f8e273c18697ff0413aa92dc7 upstream.
ntfs_create_inode() creates a new inode via ntfs_new_inode(). It hashes it with insert_inode_locked() and so it's marked as I_NEW until unlock_new_inode().
ntfs 3 calls d_instantiate() in between though... Since the dentry was already hashed by the lookup before the create any path walk finds it without touching the parent's i_rwsem and so can lock the inode.
If the inode is a directory unlock_new_inode() calls lockdep_annotate_inode_mutex_key() and marks i_rwsem with the i_mutex_dir_key class.
That resets the count and the owner of a lock somebody else may already hold by now...
syzbot has been spamming us with the same godforsaken bug
"WARNING in do_new_mount"
since 2023. I can't take it anymore so I went looking. Afaict, syzbot's executor chdirs into a freshly mounted ntfs3 image, creates a directory and then mounts some pseudofs on it. Everytime the mkdir() takes longer than syzbot waits mount() runs concurrently:
mkdir("./sys") mount(NULL, "./sys", "sysfs")
ntfs_create_inode()
d_instantiate()
user_path_at() finds the dentry
do_lock_mount()
inode_lock(inode)
namespace_lock()
unlock_new_inode()
lockdep_annotate_inode_mutex_key()
init_rwsem(&inode->i_rwsem)
unlock_mount()
inode_unlock(inode)The mount side then releases a lock that according to the rwsem nobody holds:
DEBUG_RWSEMS_WARN_ON((rwsem_owner(sem) != current) && ...):
count = 0x0, magic = 0xffff888043a854e8, owner = 0x0, curr 0xffff888000244880, list empty
WARNING: CPU: 0 PID: 5346 at kernel/locking/rwsem.c:1368 __up_write Call Trace: inode_unlock include/linux/fs.h:877 [inline] unlock_mount fs/namespace.c:2892 [inline] do_new_mount_fc fs/namespace.c:3828 [inline] do_new_mount+0x777/0xa40 fs/namespace.c:3887
On PREEMPT_RT the same thing shows up as
DEBUG_LOCKS_WARN_ON(rt_mutex_owner(lock) != current)
WARNING: kernel/locking/rtmutex_common.h:193 at rt_mutex_slowunlock
The up_write() underflows the reset count. A following inode_lock() on that directory then never returns. A path walk into the new directory racing with the mkdir() corrupts the lock the same way via inode_lock_shared() in lookup_slow().
Switch to d_instantiate_new() and drop the trailing unlock_new_inode(). All error paths bail out before that point with I_NEW still set and keep using discard_new_inode().
May we never see this fscking bug report again.
--- fs/ntfs3/inode.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-)
fs/ntfs3/inode.c +2 -5
@@ -1648,10 +1648,10 @@ int ntfs_create_inode(struct mnt_idmap * goto out6;/* -* Call 'd_instantiate' after inode->i_op is set +* Call 'd_instantiate_new' after inode->i_op is set * but before finish_open. */ -d_instantiate (dentry, inode); +d_instantiate_new (dentry, inode);/* Set original time. inode times (i_ctime) may be changed in ntfs_init_acl. */ inode_set_atime_to_ts (inode, ni->i_crtime);
@@ -1699,9 +1699,6 @@ out1:if (!fnd)ni_unlock (dir_ni); -if (!err) -unlock_new_inode (inode); -return err; }
Greg Kroah-Hartman
maintainerPATCH 259/457 netfs: Fix missing alloc tagging of direct mempool allocations
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hao Ge <[email protected]>
commit b78b728e21c32ec4c330b299f657fb1eb02dffc2 upstream.
Commit 1d78d56c43ef ("netfs: Fix folio_queue ENOMEM in writeback by adding a mempool") added a mempool for the folio_queues and made the request, subrequest and folio_queue allocations distinguish between writeback and everything else. Writeback is part of memory reclaim and must not fail due to ENOMEM, so it allocates under GFP_NOFS through mempool_alloc(), which may dip into the pool's reserve and, if that runs empty, wait for elements to be returned. The GFP_KERNEL paths, which can return -ENOMEM to their callers, invoke the pool's ->alloc() callback directly instead.
The direct call, however, skips the alloc_hooks() wrapper that the mempool_alloc() macro provides. The pool callbacks, mempool_alloc_slab() and mempool_kmalloc(), call kmem_cache_alloc_noprof() and kmalloc_noprof() and rely on current->alloc_tag having been set by the caller. With CONFIG_MEM_ALLOC_PROFILING_DEBUG=y this leads to
current->alloc_tag not set
WARNING: ./include/linux/alloc_tag.h:161 at __alloc_tagging_slab_alloc_hook alloc_tag was not set
WARNING: ./include/linux/alloc_tag.h:166 at __alloc_tagging_slab_free_hook
at allocation and free time respectively, as reported when reading files on a CIFS mount. The allocations are also missing from /proc/allocinfo.
Wrap the direct ->alloc() invocations in alloc_hooks() with a new mempool_alloc_noreserve() helper in include/linux/mempool.h, next to the other alloc_hooks()-wrapped macros such as mempool_alloc(). The GFP_KERNEL paths keep their failable allocation semantics, they just get tagged now.
--- fs/netfs/objects.c | 4 ++-- fs/netfs/rolling_buffer.c | 2 +- include/linux/mempool.h | 7 +++++++ 3 files changed, 10 insertions(+), 3 deletions(-)
fs/netfs/objects.c +2 -2
@@ -34,7 +34,7 @@ struct netfs_io_request *netfs_alloc_req rreq =mempool_alloc (mempool, gfp); }else { - rreq = mempool->alloc (gfp, mempool->pool_data); + rreq =mempool_alloc_noreserve (mempool, gfp);if (!rreq)return ERR_PTR (-ENOMEM ); }
@@ -214,7 +214,7 @@ struct netfs_io_subrequest *netfs_alloc_struct kmem_cache *cache = mempool->pool_data;if (rreq->gfp ==GFP_KERNEL ) - subreq = mempool->alloc (rreq->gfp, mempool->pool_data); + subreq =mempool_alloc_noreserve (mempool, rreq->gfp);else subreq =mempool_alloc (mempool, rreq->gfp);if (!subreq)
fs/netfs/rolling_buffer.c +1 -1
@@ -29,7 +29,7 @@ struct folio_queue *netfs_folioq_alloc(ustruct folio_queue *fq;if (gfp ==GFP_KERNEL ) - fq = netfs_folioq_pool.alloc (gfp, netfs_folioq_pool.pool_data); + fq =mempool_alloc_noreserve (&netfs_folioq_pool, gfp);else fq =mempool_alloc (&netfs_folioq_pool, gfp);if (fq) {
include/linux/mempool.h +7 -0
@@ -70,6 +70,13 @@ int mempool_alloc_bulk_noprof(struct mem#define mempool_alloc_bulk (...) \ alloc_hooks(mempool_alloc_bulk_noprof(__VA_ARGS__)) +/* +* Allocate a new element without dipping into the pool's reserves or +* waiting. Returns NULL on failure. +*/ +#define mempool_alloc_noreserve (_pool, _gfp) \ + alloc_hooks((_pool)->alloc(_gfp, (_pool)->pool_data)) +void *mempool_alloc_preallocated (struct mempool *pool) __malloc;void mempool_free (void *element,struct mempool *pool); unsigned int mempool_free_bulk(struct mempool *pool, void **elem,
Greg Kroah-Hartman
maintainerPATCH 260/457 writeback: bound cleanup_offline_cgwb() rescans by rotating scanned inodes
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Patrick Lu (Anthropic) <[email protected]>
commit f6988c90671e83db79df1b7b9d6fdb0e5947fd84 upstream.
cleanup_offline_cgwb() prepares at most WB_MAX_INODES_PER_ISW inodes per call and is called again until the dying wb is drained, but every call walks wb->b_attached and then wb->b_dirty_time from the same end. Inodes already prepared (they stay on the list with I_WB_SWITCH set until the switch worker runs) and inodes that cannot be switched (I_FREEING, I_WILL_FREE, !SB_ACTIVE, DAX, already on the target wb) stay where they are, so each pass rescans a growing run of them under wb->list_lock and a full drain is quadratic in the number of inodes on the list. With ~17M inodes attached to one dying cgwb we saw this end in soft lockups, with CPUs reported stuck for 21-48s.
Walk both lists from the oldest end and move every scanned inode to the newest end, so the next pass starts where the previous one stopped and the drain becomes linear. b_attached is unordered, so nobody sees the reorder there. b_dirty_time is ordered by dirtied_when, but the oldest unscanned inode stays at the end move_expired_inodes() picks from, sync takes the whole list regardless of order, and prepared inodes leave the list as soon as the switch work runs and get a new dirtied_time_when on the new wb anyway, so the only inodes left out of order are the ones that can never switch (DAX), and only on the dying wb.
--- fs/fs-writeback.c | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-)
fs/fs-writeback.c +20 -5
@@ -726,19 +726,34 @@ static bool isw_prepare_wbs_switch(strucstruct inode_switch_wbs_context *isw, struct list_head *list, int *nr) { - struct inode *inode; + struct inode *inode, *tmp; +LIST_HEAD (scanned); +bool full = false; + +/* +* Walk from the oldest end and move scanned inodes to the newest +* end, so the next scan resumes at unscanned inodes instead of +* re-walking an ever-growing run of prepared and skipped ones. +* For b_dirty_time this keeps the oldest unscanned inode at the +* end move_expired_inodes() picks from; b_attached is unordered. +*/ +list_for_each_entry_safe_reverse (inode, tmp, list, i_io_list) { +list_move (&inode->i_io_list, &scanned); -list_for_each_entry (inode, list, i_io_list) {if (!inode_prepare_wbs_switch (inode, new_wb))continue ; isw->inodes[*nr] = inode; (*nr)++; -if (*nr >=WB_MAX_INODES_PER_ISW -1 ) -return true; +if (*nr >=WB_MAX_INODES_PER_ISW -1 ) { + full = true; +break ; + } } -return false; +list_splice (&scanned, list); + +return full; }/**
Greg Kroah-Hartman
maintainerPATCH 261/457 workqueue: Fix NULL current_pwq deref in flush dependency check
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pavankumar Kondeti <[email protected]>
commit db6365ced4d5855e321f772b240c0e473bcfcdd5 upstream.
check_flush_dependency() uses current_wq_worker() to determine whether the caller is a workqueue worker and then dereferences worker->current_pwq to test whether the current workqueue is WQ_MEM_RECLAIM.
current_wq_worker() only means that %current has PF_WQ_WORKER set. A kworker can reach check_flush_dependency() while it is not executing a work item. One such path is worker_thread() acting as the pool manager, where create_worker() does GFP_KERNEL allocation and the allocation path invokes the OOM notifier. In that state worker->current_pwq is NULL because current_pwq is set only by process_one_work() and cleared again after the work function returns.
[ 416.760634][ T375] Call trace: [ 416.760638][ T375] check_flush_dependency+0x80/0x120 (P) [ 416.760648][ T375] __flush_work+0x98/0x224 [ 416.760657][ T375] flush_work+0x30/0x44 [ 416.760665][ T375] ... [ 416.760710][ T375] blocking_notifier_call_chain+0x58/0xa0 [ 416.760719][ T375] out_of_memory+0xb4/0x458 [ 416.760730][ T375] __alloc_pages_may_oom+0x11c/0x1a8 [ 416.760739][ T375] __alloc_pages_slowpath+0x314/0x46c [ 416.760746][ T375] __alloc_frozen_pages_noprof+0x110/0x1a4 [ 416.760753][ T375] new_slab+0x12c/0x484 [ 416.760759][ T375] ___slab_alloc+0x7a8/0xc7c [ 416.760765][ T375] __slab_alloc+0x74/0xd8 [ 416.760772][ T375] __kmalloc_cache_node_noprof+0x2ac/0x304 [ 416.760779][ T375] alloc_worker+0x28/0x60 [ 416.760785][ T375] create_worker+0x4c/0x20c [ 416.760790][ T375] worker_thread+0xe8/0x2b8 [ 416.760796][ T375] kthread+0x1a8/0x200 [ 416.760805][ T375] ret_from_fork+0x10/0x20
Guard the WQ_MEM_RECLAIM-worker warning with worker->current_pwq. If the kworker is not currently executing a work item, there is no current workqueue to diagnose with that warning. The PF_MEMALLOC warning is left unchanged so explicit reclaim context flushing a !WQ_MEM_RECLAIM target is still reported.
--- kernel/workqueue.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
kernel/workqueue.c +1 -1
@@ -3844,7 +3844,7 @@ static void check_flush_dependency(strucWARN_ONCE (current->flags &PF_MEMALLOC ,"workqueue: PF_MEMALLOC task %d(%s) is flushing !WQ_MEM_RECLAIM %s:%ps" , current->pid, current->comm, target_wq->name, target_func); - WARN_ONCE(worker && ((worker->current_pwq->wq->flags & + WARN_ONCE(worker && worker->current_pwq && ((worker->current_pwq->wq->flags & (WQ_MEM_RECLAIM | __WQ_LEGACY)) == WQ_MEM_RECLAIM), "workqueue: WQ_MEM_RECLAIM %s:%ps is flushing !WQ_MEM_RECLAIM %s:%ps", worker->current_pwq->wq->name, worker->current_func,
Greg Kroah-Hartman
maintainerPATCH 262/457 writeback: report a Tasks-RCU quiescent state per cgwb drain pass
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Josef Bacik <[email protected]>
commit 407a5d205179a4ab186571b0e16ec42725dc77bc upstream.
cleanup_offline_cgwbs_workfn() drains a dying cgwb by calling cleanup_offline_cgwb() until it returns false, with a cond_resched() between passes. On a CONFIG_PREEMPTION kernel that cond_resched() does
nothing: _cond_resched() is a plain "return 0", and under PREEMPT_DYNAMIC the full and lazy modes disable it. Since commit 7dadeaa6e851 ("sched:
Further restrict the preemption modes") those are the only two models on the architectures with PREEMPT_LAZY support, arm64 and x86 among them, so the drain loop never reports a Tasks-RCU quiescent state.
A worker draining a cgwb with millions of attached inodes runs for minutes. On a 6.18 arm64 host in lazy mode the cgwb worker drained one dying cgroup's writeback domain for over 11 minutes. A BPF program unlink (bpf_trampoline_unlink_prog -> bpf_trampoline_update -> unregister_ftrace_direct -> ftrace_shutdown -> synchronize_rcu_tasks()) waited on that grace period while holding the trampoline mutex, 42 tasks queued behind it in D state, and the hung task detector fired at 614 s and panicked the host. Any BPF or ftrace detach during a long drain inherits the drain's length.
Fix this by calling cond_resched_tasks_rcu_qs() so we do not stall out anybody who calls sycnrhonize_rcu_tasks(). We put this in a do { } while loop because if we have many small cgroups cleanup_offline_cgwb() will return false and we will never call cond_resched_tasks_rcu_qs(), creating the same problem.
--- mm/backing-dev.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
mm/backing-dev.c +3 -2
@@ -910,8 +910,9 @@ static void cleanup_offline_cgwbs_workfncontinue ;spin_unlock_irq (&cgwb_lock); -while (cleanup_offline_cgwb (wb)) -cond_resched (); +do { +cond_resched_tasks_rcu_qs (); + }while (cleanup_offline_cgwb (wb));spin_lock_irq (&cgwb_lock);wb_put (wb);