Skip to contentSkip to conversation

7.1.6-rc1 review

PATCH In review Greg Kroah-Hartman 18 participants 768 messages Jul 30 – Aug 2 Page 3 of 6

Subsystems (4)
lore
Share & tools
lore
author maintainer
#222 lore raw

PATCH 222/744 ovpn: avoid putting unrelated P2P peer on socket release

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Qing Ming <[email protected]>

[ Upstream commit b52c5103f64ee825996ca1ab8df7283cde8c5f86 ]

ovpn_peer_release_p2p() is called when an OVPN UDP socket is being destroyed. It checks the currently published P2P peer and releases it only if that peer still uses the socket being destroyed.

A peer replacement can publish a new peer before the old UDP socket is destroyed. When the old socket destruction path runs afterwards, ovpn_peer_release_p2p() observes the new peer through ovpn->peer. Since the new peer uses a different socket, the function takes the socket mismatch branch.

That branch still calls ovpn_peer_put(peer). At this point, however, peer is the currently published replacement peer, not the peer associated with the socket being destroyed. Dropping its reference can free it while ovpn->peer still points to it, leading to later use-after-free accesses from the peer and socket cleanup paths.

KASAN reports this as a slab-use-after-free on the kmalloc-1k ovpn_peer object. In the reproducer, the object is allocated from ovpn_peer_new() via ovpn_nl_peer_new_doit(), and freed through ovpn_peer_release_rcu() from RCU callback processing. Observed access sites include ovpn_peer_remove(), ovpn_socket_release(), ovpn_nl_peer_del_notify(), and unlock_ovpn().

Fix this by returning from the socket mismatch branch without putting the peer.

Fixes f6226ae7a0cd ("ovpn: introduce the ovpn_socket object")
---
 drivers/net/ovpn/peer.c | 1 -
 1 file changed, 1 deletion(-)
drivers/net/ovpn/peer.c +0 -1
index a09d61296425aa..1844d97154cea7 100644
@@ -1167,7 +1167,6 @@ static void ovpn_peer_release_p2p(struct ovpn_priv *ovpn, struct sock *sk,
 		ovpn_sock = rcu_access_pointer(peer->sock);
 		if (!ovpn_sock || ovpn_sock->sk != sk) {
 			spin_unlock_bh(&ovpn->lock);
-			ovpn_peer_put(peer);
 			return;
 		}
 	}
Signature · 1 line
2.53.0
author maintainer
#223 lore raw

PATCH 223/744 selftests/net: ovpn: fix getaddrinfo memory leak in ovpn_parse_remote()

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: longlong yan <[email protected]>

[ Upstream commit 0bd9cfebc1c91e1066e56d6261b99691b9df6008 ]

The ovpn_parse_remote() function has two memory management issues:

1. When both 'host' and 'vpnip' are non-NULL, the first getaddrinfo() allocation is leaked because 'result' is overwritten by the second getaddrinfo() call without freeing the first allocation.

2. When both 'host' and 'vpnip' are NULL, 'result' is an uninitialized stack variable passed to freeaddrinfo(), which is undefined behavior.

Fix by initializing 'result' to NULL and calling freeaddrinfo() after the first getaddrinfo() result is consumed.

Fixes 959bc330a439 ("testing/selftests: add test tool and scripts for ovpn module")
---
 tools/testing/selftests/net/ovpn/ovpn-cli.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
tools/testing/selftests/net/ovpn/ovpn-cli.c +3 -1
index d40953375c86fa..f4effa7580c0f9 100644
@@ -1785,7 +1785,7 @@ static int ovpn_parse_remote(struct ovpn_ctx *ovpn, const char *host,
 			     const char *service, const char *vpnip)
 {
 	int ret;
-	struct addrinfo *result;
+	struct addrinfo *result = NULL;
 	struct addrinfo hints = {
 		.ai_family = ovpn->sa_family,
 		.ai_socktype = SOCK_DGRAM,
@@ -1809,6 +1809,8 @@ static int ovpn_parse_remote(struct ovpn_ctx *ovpn, const char *host,
 		}
 
 		memcpy(&ovpn->remote, result->ai_addr, result->ai_addrlen);
+		freeaddrinfo(result);
+		result = NULL;
 	}
 
 	if (vpnip) {
Signature · 1 line
2.53.0
author maintainer
#224 lore raw

PATCH 224/744 ovpn: fix use after free in unlock_ovpn()

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Marco Baffo <[email protected]>

[ Upstream commit e1ad6fe5db719874efa45b2caf9934552e09fc43 ]

unlock_ovpn() iterates over the release_list using llist_for_each_entry() and drops the peer reference inside the loop body via ovpn_peer_put().

If this drops the last reference, the peer is eventually freed. However, llist_for_each_entry() reads peer->release_entry.next in the loop advance expression, which runs after the body. By that time the peer may have already been freed, resulting in a use after free when advancing to the next list entry.

Fix this by using llist_for_each_entry_safe(), which caches the next pointer before executing the loop body.

Fixes 80747caef33d ("ovpn: introduce the ovpn_peer object")
---
 drivers/net/ovpn/peer.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)
drivers/net/ovpn/peer.c +3 -2
index 1844d97154cea7..7dedbe54a37cfb 100644
@@ -26,11 +26,12 @@ static void unlock_ovpn(struct ovpn_priv *ovpn,
 			 struct llist_head *release_list)
 	__releases(&ovpn->lock)
 {
-	struct ovpn_peer *peer;
+	struct ovpn_peer *peer, *next;
 
 	spin_unlock_bh(&ovpn->lock);
 
-	llist_for_each_entry(peer, release_list->first, release_entry) {
+	llist_for_each_entry_safe(peer, next, release_list->first,
+				  release_entry) {
 		ovpn_socket_release(peer);
 		ovpn_peer_put(peer);
 	}
Signature · 1 line
2.53.0
author maintainer
#225 lore raw

PATCH 225/744 ovpn: use monotonic clock for peer keepalive timeouts

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Marco Baffo <[email protected]>

[ Upstream commit f7e6287ccd3abeed9e638b581dc3fdf742106ba3 ]

Replace ktime_get_real_seconds() with the monotonic ktime_get_boottime_seconds() to ensure the keepalive mechanism is robust against system clock modifications.

Right now, the driver uses ktime_get_real_seconds() to track peer timeouts, relying on the system wall-clock.

An administrative time adjustment or an NTP sync that steps the clock forward can cause now' to instantly exceed last_recv + timeout'.

When this occurs, the driver artificially expires healthy peers. Depending on the OpenVPN user-space configuration, this triggers a premature tunnel restart (if --keepalive or --ping-restart is used) or a complete disconnection of the client (if --ping-exit is used).

Fixes 3ecfd9349f40 ("ovpn: implement keepalive mechanism")
---
 drivers/net/ovpn/io.c   | 4 ++--
 drivers/net/ovpn/peer.c | 4 ++--
 2 files changed, 4 insertions(+), 4 deletions(-)
drivers/net/ovpn/io.c +2 -2
index a6b777a9c2d9a2..9a66d693039a3c 100644
@@ -142,7 +142,7 @@ void ovpn_decrypt_post(void *data, int ret)
 	}
 
 	/* keep track of last received authenticated packet for keepalive */
-	WRITE_ONCE(peer->last_recv, ktime_get_real_seconds());
+	WRITE_ONCE(peer->last_recv, ktime_get_boottime_seconds());
 
 	rcu_read_lock();
 	sock = rcu_dereference(peer->sock);
@@ -294,7 +294,7 @@ void ovpn_encrypt_post(void *data, int ret)
 
 	ovpn_peer_stats_increment_tx(&peer->link_stats, orig_len);
 	/* keep track of last sent packet for keepalive */
-	WRITE_ONCE(peer->last_sent, ktime_get_real_seconds());
+	WRITE_ONCE(peer->last_sent, ktime_get_boottime_seconds());
 	/* skb passed down the stack - don't free it */
 	skb = NULL;
 err_unlock:
drivers/net/ovpn/peer.c +2 -2
index 7dedbe54a37cfb..9c82078146bbaf 100644
@@ -45,7 +45,7 @@ static void unlock_ovpn(struct ovpn_priv *ovpn,
  */
 void ovpn_peer_keepalive_set(struct ovpn_peer *peer, u32 interval, u32 timeout)
 {
-	time64_t now = ktime_get_real_seconds();
+	time64_t now = ktime_get_boottime_seconds();
 
 	netdev_dbg(peer->ovpn->dev,
 		   "scheduling keepalive for peer %u: interval=%u timeout=%u\n",
@@ -1357,7 +1357,7 @@ void ovpn_peer_keepalive_work(struct work_struct *work)
 {
 	struct ovpn_priv *ovpn = container_of(work, struct ovpn_priv,
 					      keepalive_work.work);
-	time64_t next_run = 0, now = ktime_get_real_seconds();
+	time64_t next_run = 0, now = ktime_get_boottime_seconds();
 	LLIST_HEAD(release_list);
 
 	spin_lock_bh(&ovpn->lock);
Signature · 1 line
2.53.0
author maintainer
#226 lore raw

PATCH 226/744 hwmon: occ: validate poll response sensor blocks

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Pengpeng Hou <[email protected]>

[ Upstream commit 70e76e700fc6c46afb4e17aec099a1ea089b4a22 ]

The OCC poll response parser walks a counted list of sensor data blocks. It used the static backing-array capacity as the parse boundary, but a transport response makes only data_length bytes current and valid. A truncated response can therefore make the parser consume a block header or block extent outside the current response.

Use data_length as the parent boundary, prove the fixed poll header and each current block header before reading them, and prove the complete block before advancing. Keep parsed sensor metadata local until the complete response has passed validation, then publish it. Propagate malformed-response errors before publishing the OCC as active.

Fixes aa195fe49b03 ("hwmon (occ): Parse OCC poll response")
---
 drivers/hwmon/occ/common.c | 38 ++++++++++++++++++++++++++++++--------
 1 file changed, 30 insertions(+), 8 deletions(-)
drivers/hwmon/occ/common.c +30 -8
index e18e80e832fd3f..175208d712b06e 100644
@@ -1052,32 +1052,49 @@ static int occ_setup_sensor_attrs(struct occ *occ)
 }
 
 /* only need to do this once at startup, as OCC won't change sensors on us */
-static void occ_parse_poll_response(struct occ *occ)
+static int occ_parse_poll_response(struct occ *occ)
 {
 	unsigned int i, old_offset, offset = 0, size = 0;
+	u16 data_length;
 	struct occ_sensor *sensor;
-	struct occ_sensors *sensors = &occ->sensors;
+	struct occ_sensors parsed = {};
+	struct occ_sensors *sensors = &parsed;
 	struct occ_response *resp = &occ->resp;
 	struct occ_poll_response *poll =
 		(struct occ_poll_response *)&resp->data[0];
 	struct occ_poll_response_header *header = &poll->header;
 	struct occ_sensor_data_block *block = &poll->block;
 
+	data_length = get_unaligned_be16(&resp->data_length);
+	if (data_length < sizeof(*header) || data_length > OCC_RESP_DATA_BYTES) {
+		dev_err(occ->bus_dev, "invalid OCC poll response length %u\n",
+			data_length);
+		return -EMSGSIZE;
+	}
+
 	dev_info(occ->bus_dev, "OCC found, code level: %.16s\n",
 		 header->occ_code_level);
 
 	for (i = 0; i < header->num_sensor_data_blocks; ++i) {
 		block = (struct occ_sensor_data_block *)((u8 *)block + offset);
+		if (size + sizeof(*header) + sizeof(block->header) >
+		    data_length) {
+			dev_err(occ->bus_dev,
+				"truncated OCC sensor block header\n");
+			return -EMSGSIZE;
+		}
+
 		old_offset = offset;
 		offset = (block->header.num_sensors *
 			  block->header.sensor_length) + sizeof(block->header);
-		size += offset;
 
 		/* validate all the length/size fields */
-		if ((size + sizeof(*header)) >= OCC_RESP_DATA_BYTES) {
-			dev_warn(occ->bus_dev, "exceeded response buffer\n");
-			return;
+		if (size + sizeof(*header) + offset > data_length) {
+			dev_err(occ->bus_dev,
+				"exceeded OCC poll response length\n");
+			return -EMSGSIZE;
 		}
+		size += offset;
 
 		dev_dbg(occ->bus_dev, " %04x..%04x: %.4s (%d sensors)\n",
 			old_offset, offset - 1, block->header.eye_catcher,
@@ -1107,6 +1124,9 @@ static void occ_parse_poll_response(struct occ *occ)
 
 	dev_dbg(occ->bus_dev, "Max resp size: %u+%zd=%zd\n", size,
 		sizeof(*header), size + sizeof(*header));
+	occ->sensors = parsed;
+
+	return 0;
 }
 
 int occ_active(struct occ *occ, bool active)
@@ -1138,10 +1158,12 @@ int occ_active(struct occ *occ, bool active)
 			goto unlock;
 		}
 
-		occ->active = true;
 		occ->next_update = jiffies + OCC_UPDATE_FREQUENCY;
-		occ_parse_poll_response(occ);
+		rc = occ_parse_poll_response(occ);
+		if (rc)
+			goto unlock;
 
+		occ->active = true;
 		rc = occ_setup_sensor_attrs(occ);
 		if (rc) {
 			dev_err(occ->bus_dev,
Signature · 1 line
2.53.0
author maintainer
#227 lore raw

PATCH 227/744 regulator: mt6358: use regmap helper to read fixed LDO calibration

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Daniel Golle <[email protected]>

[ Upstream commit 1d26f125501f3fbe6c259ab75bf6516299a0bf0e ]

The "fixed" LDOs with output voltage calibration use mt6358_get_buck_voltage_sel as their get_voltage_sel op, but the MT6358_REG_FIXED and MT6366_REG_FIXED entries do not populate da_vsel_reg/da_vsel_mask. The op therefore reads register 0x0 with a zero mask and shifts the result by ffs(0) - 1 = -1, which is undefined behaviour and gets flagged by UBSAN on every boot on MT6366 boards:

  UBSAN: shift-out-of-bounds in drivers/regulator/mt6358-regulator.c:384:38
  shift exponent -1 is negative
  Call trace:
   mt6358_get_buck_voltage_sel+0xc8/0x120
   regulator_get_voltage_rdev+0x70/0x170
   set_machine_constraints+0x504/0xc38
   regulator_register+0x324/0xc68

Besides the undefined shift, the returned selector is always 0, so the actual calibration offset programmed in <reg>_ANA_CON0 is never reported.

The descriptor already carries the correct vsel_reg/vsel_mask (the ANA_CON0 calibration field), matching the regulator_set_voltage_sel_regmap op already in use. Read the selector back through regulator_get_voltage_sel_regmap instead.

---
 drivers/regulator/mt6358-regulator.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/regulator/mt6358-regulator.c +1 -1
index 2604f674be493b..d483f85936f81f 100644
@@ -492,7 +492,7 @@ static const struct regulator_ops mt6358_volt_fixed_ops = {
 	.list_voltage = regulator_list_voltage_linear,
 	.map_voltage = regulator_map_voltage_linear,
 	.set_voltage_sel = regulator_set_voltage_sel_regmap,
-	.get_voltage_sel = mt6358_get_buck_voltage_sel,
+	.get_voltage_sel = regulator_get_voltage_sel_regmap,
 	.set_voltage_time_sel = regulator_set_voltage_time_sel,
 	.enable = regulator_enable_regmap,
 	.disable = regulator_disable_regmap,
Signature · 1 line
2.53.0
author maintainer
#228 lore raw

PATCH 228/744 Bluetooth: btusb: validate Realtek vendor event length

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Pengpeng Hou <[email protected]>

[ Upstream commit df541cd485ff80a5ddc579d99687bc7506df9851 ]

btusb_recv_event_realtek() reads the event code at data[0] and the Realtek subevent code at data[2] before deciding whether to consume a vendor event as a coredump.

For example, the two-byte event ff 00 contains a complete vendor-event header declaring zero parameters. The old classifier still reads a nonexistent third byte and can misclassify the event as a coredump if the adjacent byte is 0x34.

Require the HCI event header and first parameter to be present before inspecting the Realtek subevent code. Short events continue through the normal HCI receive path, which owns their protocol validation.

Fixes 044014ce85a1 ("Bluetooth: btrtl: Add Realtek devcoredump support")
---
 drivers/bluetooth/btusb.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/bluetooth/btusb.c +3 -1
index cc6392f8d98cd2..7f66e7c6e77ad4 100644
@@ -2765,7 +2765,9 @@ static int btusb_setup_realtek(struct hci_dev *hdev)
 
 static int btusb_recv_event_realtek(struct hci_dev *hdev, struct sk_buff *skb)
 {
-	if (skb->data[0] == HCI_VENDOR_PKT && skb->data[2] == RTK_SUB_EVENT_CODE_COREDUMP) {
+	if (skb->len >= HCI_EVENT_HDR_SIZE + 1 &&
+	    skb->data[0] == HCI_VENDOR_PKT &&
+	    skb->data[2] == RTK_SUB_EVENT_CODE_COREDUMP) {
 		struct rtk_dev_coredump_hdr hdr = {
 			.code = RTK_DEVCOREDUMP_CODE_MEMDUMP,
 		};
Signature · 1 line
2.53.0
author maintainer
#229 lore raw

PATCH 229/744 net: phy: marvell: fix return code

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Michael Walle <[email protected]>

[ Upstream commit 7d8ca62d6a9ef593780161586b4efc811ac094fe ]

Return the correct error code, not the value written to the register.

Fixes a219912e0fec ("net: phy: marvell: implement config_inband() method")
---
 drivers/net/phy/marvell.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/phy/marvell.c +1 -1
index 7a578b5aa2ed60..f71cffa8840628 100644
@@ -753,7 +753,7 @@ static int m88e1111_config_inband(struct phy_device *phydev, unsigned int modes)
 	err = phy_modify(phydev, MII_M1111_PHY_EXT_SR,
 			 MII_M1111_HWCFG_SERIAL_AN_BYPASS, extsr);
 	if (err < 0)
-		return extsr;
+		return err;
 
 	return phy_modify_paged(phydev, MII_MARVELL_FIBER_PAGE, MII_BMCR,
 				BMCR_ANENABLE, bmcr);
Signature · 1 line
2.53.0
author maintainer
#230 lore raw

PATCH 230/744 selftests/net: Fix tun IPv6 test addresses to avoid 6to4 range

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Ricardo B. Marlière <[email protected]>

[ Upstream commit ef01724fa235a228e3d3e8b117e89403cd8feb25 ]

The IPv6 addresses used for the tun_vnet_udptnl fixture currently fall in the 2002::/16 prefix, which is reserved for the 6to4 transition mechanism (RFC 3056).

On systems where the sit module is loaded, the kernel automatically claims 2002::/16 as a 6to4 tunnel prefix. When the test assigns a 2002:: address to a TUN interface, sit registers a competing local route for the same address. This ambiguity breaks the GENEVE decapsulation path: packets injected via the TUN fd are not delivered to the test socket, causing the IPv6-outer gtgso send_gso_packet variants to fail.

Replace all four IPv6 test addresses with addresses from the fd00:db8::/32 range, which is part of the ULA space (fc00::/7, RFC 4193) and carries no special kernel semantics.

Fixes 24e59f26eef2 ("selftest: tun: Add helpers for GSO over UDP tunnel")
---
 tools/testing/selftests/net/tun.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)
tools/testing/selftests/net/tun.c +4 -4
index cf106a49b55e38..abe488bac50bb3 100644
@@ -42,19 +42,19 @@ static struct in_addr param_ipaddr4_inner_src = {
 };
 
 static struct in6_addr param_ipaddr6_outer_dst = {
-	{ { 0x20, 0x02, 0x0d, 0xb8, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 } },
+	{ { 0xfd, 0x00, 0x0d, 0xb8, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 } },
 };
 
 static struct in6_addr param_ipaddr6_outer_src = {
-	{ { 0x20, 0x02, 0x0d, 0xb8, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2 } },
+	{ { 0xfd, 0x00, 0x0d, 0xb8, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2 } },
 };
 
 static struct in6_addr param_ipaddr6_inner_dst = {
-	{ { 0x20, 0x02, 0x0d, 0xb8, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 } },
+	{ { 0xfd, 0x00, 0x0d, 0xb8, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 } },
 };
 
 static struct in6_addr param_ipaddr6_inner_src = {
-	{ { 0x20, 0x02, 0x0d, 0xb8, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2 } },
+	{ { 0xfd, 0x00, 0x0d, 0xb8, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2 } },
 };
 
 #ifndef BIT
Signature · 1 line
2.53.0
author maintainer
#231 lore raw

PATCH 231/744 netlink: specs: rt-link: convert bridge port flag attributes to u8

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Danielle Ratson <[email protected]>

[ Upstream commit f6e3b21608e974c4aaa4cfd73a239dacf1d8a9a3 ]

A number of IFLA_BRPORT_* attributes are documented in the rt-link spec as having the "flag" type, i.e. a payload-less NLA_FLAG attribute whose meaning is presence-only. This does not match the kernel, which emits these attributes with nla_put_u8() and validates them as NLA_U8 in br_port_policy[]. The values are not mere presence flags but carry a u8 payload (0/1).

Convert these bridge port attributes from "flag" to "u8" so the spec reflects the actual wire format.

---
 Documentation/netlink/specs/rt-link.yaml | 40 ++++++++++++------------
 1 file changed, 20 insertions(+), 20 deletions(-)
Documentation/netlink/specs/rt-link.yaml +20 -20
index 644a8bd7b93c34..e656f05268ed26 100644
@@ -1583,31 +1583,31 @@ attribute-sets:
         type: u32
       -
         name: mode
-        type: flag
+        type: u8
       -
         name: guard
-        type: flag
+        type: u8
       -
         name: protect
-        type: flag
+        type: u8
       -
         name: fast-leave
-        type: flag
+        type: u8
       -
         name: learning
-        type: flag
+        type: u8
       -
         name: unicast-flood
-        type: flag
+        type: u8
       -
         name: proxyarp
-        type: flag
+        type: u8
       -
         name: learning-sync
-        type: flag
+        type: u8
       -
         name: proxyarp-wifi
-        type: flag
+        type: u8
       -
         name: root-id
         type: binary
@@ -1654,34 +1654,34 @@ attribute-sets:
         type: pad
       -
         name: mcast-flood
-        type: flag
+        type: u8
       -
         name: mcast-to-ucast
-        type: flag
+        type: u8
       -
         name: vlan-tunnel
-        type: flag
+        type: u8
       -
         name: bcast-flood
-        type: flag
+        type: u8
       -
         name: group-fwd-mask
         type: u16
       -
         name: neigh-suppress
-        type: flag
+        type: u8
       -
         name: isolated
-        type: flag
+        type: u8
       -
         name: backup-port
         type: u32
       -
         name: mrp-ring-open
-        type: flag
+        type: u8
       -
         name: mrp-in-open
-        type: flag
+        type: u8
       -
         name: mcast-eht-hosts-limit
         type: u32
@@ -1690,10 +1690,10 @@ attribute-sets:
         type: u32
       -
         name: locked
-        type: flag
+        type: u8
       -
         name: mab
-        type: flag
+        type: u8
       -
         name: mcast-n-groups
         type: u32
@@ -1702,7 +1702,7 @@ attribute-sets:
         type: u32
       -
         name: neigh-vlan-suppress
-        type: flag
+        type: u8
       -
         name: backup-nhid
         type: u32
Signature · 1 line
2.53.0
author maintainer
#232 lore raw

PATCH 232/744 net/packet: avoid fanout hook re-registration after unregister

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: David Lee <[email protected]>

[ Upstream commit 50aff80475abd3533eef4320477037e6fcc6b56e ]

packet_set_ring() temporarily detaches a socket from packet delivery while reconfiguring its ring. It records the previous running state, clears po->num, unregisters the protocol hook when needed, drops po->bind_lock, and later restores po->num and re-registers the hook from the saved was_running value.

That unlocked window can race with NETDEV_UNREGISTER. The notifier can observe the socket as not running, skip __unregister_prot_hook(), and invalidate the per-socket binding by setting po->ifindex to -1 and clearing po->prot_hook.dev. A one-member fanout group can still retain its shared fanout hook device pointer. When packet_set_ring() resumes, re-registering solely from the stale was_running state can re-add the fanout hook after the device has been unregistered.

Treat po->ifindex == -1 as an invalidated binding after reacquiring po->bind_lock. This is distinct from ifindex 0, the normal unbound/wildcard state: ifindex -1 marks an existing device binding that was invalidated when the device was unregistered. Restore po->num as before, but do not re-register the hook if device unregister already detached the socket.

---
 net/packet/af_packet.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)
net/packet/af_packet.c +5 -1
index 8e6f3a734ba0bd..e75d2932475ac0 100644
@@ -4561,7 +4561,11 @@ static int packet_set_ring(struct sock *sk, union tpacket_req_u *req_u,
 
 	spin_lock(&po->bind_lock);
 	WRITE_ONCE(po->num, num);
-	if (was_running)
+	/*
+	 * NETDEV_UNREGISTER may have invalidated the binding while bind_lock
+	 * was dropped above.  Do not re-add a fanout hook to a dead device.
+	 */
+	if (was_running && READ_ONCE(po->ifindex) != -1)
 		register_prot_hook(sk);
 
 	spin_unlock(&po->bind_lock);
Signature · 1 line
2.53.0
author maintainer
#233 lore raw

PATCH 233/744 bpf: Reject redirect helpers without a bpf_net_context

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Daniel Borkmann <[email protected]>

[ Upstream commit 3f4920d165b29052255527d8ae7619e7ec132ece ]

The bpf_redirect*() helpers and skb_do_redirect() obtain the per-task bpf_redirect_info via bpf_net_ctx_get_ri(), which dereferences the current->bpf_net_context unconditionally. That context is established on the paths that run tc BPF such as sch_handle_{ingress,egress}(), *except* for the case where {cls,act}_bpf was attached to a proper qdisc. A program running from there reaches the NULL deref in two ways:

* It calls bpf_redirect() directly, which dereferences the context at the top of the helper:

tc qdisc add dev eth0 root handle 1: red limit 1MB min 10KB max 20KB \
avpkt 1000 burst 100 qevent early_drop block 10
tc filter add block 10 pref 1 bpf obj redirect.o

* It simply returns TC_ACT_REDIRECT without helper call: tcf_qevent_handle() then dispatches to skb_do_redirect(), which dereferences the context

Rather than extending bpf_net_context management into the qdisc path, make the redirect helpers refuse to operate when no context exists, and have tcf_qevent_handle() drop a TC_ACT_REDIRECT verdict instead of calling skb_do_redirect(). Previous behaviour was a crash, so nothing regresses by not supporting it.

Fixes 401cb7dae813 ("net: Reference bpf_redirect_info via task_struct on PREEMPT_RT.")
Fixes 3625750f05ec ("net: sched: Introduce helpers for qevent blocks")
---
 net/core/filter.c   | 17 +++++++++++------
 net/sched/cls_api.c |  6 ++----
 2 files changed, 13 insertions(+), 10 deletions(-)
net/core/filter.c +11 -6
index f21aeff99cc983..66837e93d3f25d 100644
@@ -2552,11 +2552,13 @@ int skb_do_redirect(struct sk_buff *skb)
 
 BPF_CALL_2(bpf_redirect, u32, ifindex, u64, flags)
 {
-	struct bpf_redirect_info *ri = bpf_net_ctx_get_ri();
+	struct bpf_redirect_info *ri;
 
-	if (unlikely(flags & (~(BPF_F_INGRESS) | BPF_F_REDIRECT_INTERNAL)))
+	if (unlikely(!bpf_net_ctx_get() ||
+		     (flags & (~(BPF_F_INGRESS) | BPF_F_REDIRECT_INTERNAL))))
 		return TC_ACT_SHOT;
 
+	ri = bpf_net_ctx_get_ri();
 	ri->flags = flags;
 	ri->tgt_index = ifindex;
 
@@ -2573,11 +2575,12 @@ static const struct bpf_func_proto bpf_redirect_proto = {
 
 BPF_CALL_2(bpf_redirect_peer, u32, ifindex, u64, flags)
 {
-	struct bpf_redirect_info *ri = bpf_net_ctx_get_ri();
+	struct bpf_redirect_info *ri;
 
-	if (unlikely(flags))
+	if (unlikely(!bpf_net_ctx_get() || flags))
 		return TC_ACT_SHOT;
 
+	ri = bpf_net_ctx_get_ri();
 	ri->flags = BPF_F_PEER;
 	ri->tgt_index = ifindex;
 
@@ -2595,11 +2598,13 @@ static const struct bpf_func_proto bpf_redirect_peer_proto = {
 BPF_CALL_4(bpf_redirect_neigh, u32, ifindex, struct bpf_redir_neigh *, params,
 	   int, plen, u64, flags)
 {
-	struct bpf_redirect_info *ri = bpf_net_ctx_get_ri();
+	struct bpf_redirect_info *ri;
 
-	if (unlikely((plen && plen < sizeof(*params)) || flags))
+	if (unlikely((plen && plen < sizeof(*params)) ||
+		     !bpf_net_ctx_get() || flags))
 		return TC_ACT_SHOT;
 
+	ri = bpf_net_ctx_get_ri();
 	ri->flags = BPF_F_NEIGH | (plen ? BPF_F_NEXTHOP : 0);
 	ri->tgt_index = ifindex;
 
net/sched/cls_api.c +2 -4
index ffeea6db833747..523cf2a8bd1d6d 100644
@@ -4046,6 +4046,8 @@ struct sk_buff *tcf_qevent_handle(struct tcf_qevent *qe, struct Qdisc *sch, stru
 	fl = rcu_dereference_bh(qe->filter_chain);
 
 	switch (tcf_classify(skb, NULL, fl, &cl_res, false)) {
+	case TC_ACT_REDIRECT:
+		fallthrough;
 	case TC_ACT_SHOT:
 		qdisc_qstats_drop(sch);
 		__qdisc_drop(skb, to_free);
@@ -4057,10 +4059,6 @@ struct sk_buff *tcf_qevent_handle(struct tcf_qevent *qe, struct Qdisc *sch, stru
 		__qdisc_drop(skb, to_free);
 		*ret = __NET_XMIT_STOLEN;
 		return NULL;
-	case TC_ACT_REDIRECT:
-		skb_do_redirect(skb);
-		*ret = __NET_XMIT_STOLEN;
-		return NULL;
 	case TC_ACT_CONSUMED:
 		*ret = __NET_XMIT_STOLEN;
 		return NULL;
Signature · 1 line
2.53.0
author maintainer
#234 lore raw

PATCH 234/744 net/sched: Handle TC_ACT_REDIRECT from qdisc filter chains

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Jamal Hadi Salim <[email protected]>

[ Upstream commit ec48b3be2c8595dd290be883dbd4fb8b2f9f5d5e ]

When a TC filter attached to a qdisc filter chain returns TC_ACT_REDIRECT (ex: via an eBPF program calling bpf_redirect() or an act_bpf action), the redirect was silently lost i.e no qdisc classify function handled TC_ACT_REDIRECT, so the packet fell through the switch and was enqueued normally instead of being redirected.

This has been broken since bpf_redirect() was introduced for TC in commit 27b29f63058d ("bpf: add bpf_redirect() helper"). We got lucky for a long time because bpf_net_context was a per-CPU variable that was always available.

commit 401cb7dae813 ("net: Reference bpf_redirect_info via task_struct on PREEMPT_RT.") turned bpf_net_context into a task_struct member that is only set up by explicit callers. Without a caller setting it up, bpf_redirect() itself crashes with a NULL pointer dereference in bpf_net_ctx_get_ri(). However, even with bpf_net_context available, TC_ACT_REDIRECT from qdisc filter chains cannot be honored without adding skb_do_redirect() calls to every qdisc classify function, which would require changes across net/sched/. Isolate it to ebpf core where it belongs.

Instead, add a tcf_classify_qdisc() inline helper in pkt_cls.h, as a wrapper around tcf_classify() for use by qdisc classify functions and tcf_qevent_handle(). When the classify verdict is TC_ACT_REDIRECT, the wrapper converts it to TC_ACT_SHOT, dropping the packet rather than letting it continue silently. Dropping is preferred over letting the packet through because the user immediately sees packet loss. Silently passing the packet through would hide the problem and leave the user wondering why their redirect is not working.

The clsact fast path, tc_run() continues to call tcf_classify() directly and is unaffected: TC_ACT_REDIRECT is returned as-is and handled by sch_handle_egress/ingress() calling skb_do_redirect() as before.

Fixes 27b29f63058d ("bpf: add bpf_redirect() helper")
Fixes 401cb7dae813 ("net: Reference bpf_redirect_info via task_struct on PREEMPT_RT.")
---
 include/net/pkt_cls.h    | 14 +++++++++++++-
 net/sched/cls_api.c      |  4 +---
 net/sched/sch_cake.c     |  2 +-
 net/sched/sch_drr.c      |  2 +-
 net/sched/sch_dualpi2.c  |  2 +-
 net/sched/sch_ets.c      |  2 +-
 net/sched/sch_fq_codel.c |  2 +-
 net/sched/sch_fq_pie.c   |  2 +-
 net/sched/sch_hfsc.c     |  2 +-
 net/sched/sch_htb.c      |  2 +-
 net/sched/sch_multiq.c   |  2 +-
 net/sched/sch_prio.c     |  2 +-
 net/sched/sch_qfq.c      |  2 +-
 net/sched/sch_sfb.c      |  2 +-
 net/sched/sch_sfq.c      |  2 +-
 15 files changed, 27 insertions(+), 17 deletions(-)
include/net/pkt_cls.h +13 -1
index 99ac747b790607..7f0c422307f455 100644
@@ -156,8 +156,20 @@ static inline int tcf_classify(struct sk_buff *skb,
 {
 	return TC_ACT_UNSPEC;
 }
-
 #endif
+static inline int tcf_classify_qdisc(struct sk_buff *skb,
+				     const struct tcf_proto *tp,
+				     struct tcf_result *res, bool compat_mode)
+{
+	int ret = tcf_classify(skb, NULL, tp, res, compat_mode);
+
+	/* TC_ACT_REDIRECT from qdisc filter chains is not supported.
+	 * Use BPF via tcx or mirred redirect instead.
+	 */
+	if (unlikely(ret == TC_ACT_REDIRECT))
+		ret = TC_ACT_SHOT;
+	return ret;
+}
 
 static inline unsigned long
 __cls_set_class(unsigned long *clp, unsigned long cl)
net/sched/cls_api.c +1 -3
index 523cf2a8bd1d6d..fee4524adc98ef 100644
@@ -4045,9 +4045,7 @@ struct sk_buff *tcf_qevent_handle(struct tcf_qevent *qe, struct Qdisc *sch, stru
 
 	fl = rcu_dereference_bh(qe->filter_chain);
 
-	switch (tcf_classify(skb, NULL, fl, &cl_res, false)) {
-	case TC_ACT_REDIRECT:
-		fallthrough;
+	switch (tcf_classify_qdisc(skb, fl, &cl_res, false)) {
 	case TC_ACT_SHOT:
 		qdisc_qstats_drop(sch);
 		__qdisc_drop(skb, to_free);
net/sched/sch_cake.c +1 -1
index 07ea175657bb8e..b967b7153ad34e 100644
@@ -1727,7 +1727,7 @@ static u32 cake_classify(struct Qdisc *sch, struct cake_tin_data **t,
 		goto hash;
 
 	*qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
-	result = tcf_classify(skb, NULL, filter, &res, false);
+	result = tcf_classify_qdisc(skb, filter, &res, false);
 
 	if (result >= 0) {
 #ifdef CONFIG_NET_CLS_ACT
net/sched/sch_drr.c +1 -1
index 2eedd3a4322ad5..c7d489feec614a 100644
@@ -314,7 +314,7 @@ static struct drr_class *drr_classify(struct sk_buff *skb, struct Qdisc *sch,
 
 	*qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
 	fl = rcu_dereference_bh(q->filter_list);
-	result = tcf_classify(skb, NULL, fl, &res, false);
+	result = tcf_classify_qdisc(skb, fl, &res, false);
 	if (result >= 0) {
 #ifdef CONFIG_NET_CLS_ACT
 		switch (result) {
net/sched/sch_dualpi2.c +1 -1
index 27088760eff443..4f678d4ff10ec7 100644
@@ -364,7 +364,7 @@ static int dualpi2_skb_classify(struct dualpi2_sched_data *q,
 	if (!fl)
 		return NET_XMIT_SUCCESS;
 
-	result = tcf_classify(skb, NULL, fl, &res, false);
+	result = tcf_classify_qdisc(skb, fl, &res, false);
 	if (result >= 0) {
 #ifdef CONFIG_NET_CLS_ACT
 		switch (result) {
net/sched/sch_ets.c +1 -1
index c817e0a6c14653..dc72a7a44813ad 100644
@@ -393,7 +393,7 @@ static struct ets_class *ets_classify(struct sk_buff *skb, struct Qdisc *sch,
 	*qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
 	if (TC_H_MAJ(skb->priority) != sch->handle) {
 		fl = rcu_dereference_bh(q->filter_list);
-		err = tcf_classify(skb, NULL, fl, &res, false);
+		err = tcf_classify_qdisc(skb, fl, &res, false);
 #ifdef CONFIG_NET_CLS_ACT
 		switch (err) {
 		case TC_ACT_STOLEN:
net/sched/sch_fq_codel.c +1 -1
index 09ab84580160ae..ac4eeb5fe51cc0 100644
@@ -91,7 +91,7 @@ static unsigned int fq_codel_classify(struct sk_buff *skb, struct Qdisc *sch,
 		return fq_codel_hash(q, skb) + 1;
 
 	*qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
-	result = tcf_classify(skb, NULL, filter, &res, false);
+	result = tcf_classify_qdisc(skb, filter, &res, false);
 	if (result >= 0) {
 #ifdef CONFIG_NET_CLS_ACT
 		switch (result) {
net/sched/sch_fq_pie.c +1 -1
index 0a4eca4ab086eb..1815f93abb1359 100644
@@ -96,7 +96,7 @@ static unsigned int fq_pie_classify(struct sk_buff *skb, struct Qdisc *sch,
 		return fq_pie_hash(q, skb) + 1;
 
 	*qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
-	result = tcf_classify(skb, NULL, filter, &res, false);
+	result = tcf_classify_qdisc(skb, filter, &res, false);
 	if (result >= 0) {
 #ifdef CONFIG_NET_CLS_ACT
 		switch (result) {
net/sched/sch_hfsc.c +1 -1
index 50112c8a71e033..33801faf16209c 100644
@@ -1143,7 +1143,7 @@ hfsc_classify(struct sk_buff *skb, struct Qdisc *sch, int *qerr)
 	*qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
 	head = &q->root;
 	tcf = rcu_dereference_bh(q->root.filter_list);
-	while (tcf && (result = tcf_classify(skb, NULL, tcf, &res, false)) >= 0) {
+	while (tcf && (result = tcf_classify_qdisc(skb, tcf, &res, false)) >= 0) {
 #ifdef CONFIG_NET_CLS_ACT
 		switch (result) {
 		case TC_ACT_QUEUED:
net/sched/sch_htb.c +1 -1
index ccabafed3fe44a..75b9e534a8063b 100644
@@ -242,7 +242,7 @@ static struct htb_class *htb_classify(struct sk_buff *skb, struct Qdisc *sch,
 	}
 
 	*qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
-	while (tcf && (result = tcf_classify(skb, NULL, tcf, &res, false)) >= 0) {
+	while (tcf && (result = tcf_classify_qdisc(skb, tcf, &res, false)) >= 0) {
 #ifdef CONFIG_NET_CLS_ACT
 		switch (result) {
 		case TC_ACT_QUEUED:
net/sched/sch_multiq.c +1 -1
index a467dd12236970..66df30939aa5c1 100644
@@ -36,7 +36,7 @@ multiq_classify(struct sk_buff *skb, struct Qdisc *sch, int *qerr)
 	int err;
 
 	*qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
-	err = tcf_classify(skb, NULL, fl, &res, false);
+	err = tcf_classify_qdisc(skb, fl, &res, false);
 #ifdef CONFIG_NET_CLS_ACT
 	switch (err) {
 	case TC_ACT_STOLEN:
net/sched/sch_prio.c +1 -1
index fe42ae3d6b696b..9da76296177aef 100644
@@ -39,7 +39,7 @@ prio_classify(struct sk_buff *skb, struct Qdisc *sch, int *qerr)
 	*qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
 	if (TC_H_MAJ(skb->priority) != sch->handle) {
 		fl = rcu_dereference_bh(q->filter_list);
-		err = tcf_classify(skb, NULL, fl, &res, false);
+		err = tcf_classify_qdisc(skb, fl, &res, false);
 #ifdef CONFIG_NET_CLS_ACT
 		switch (err) {
 		case TC_ACT_STOLEN:
net/sched/sch_qfq.c +1 -1
index 195c434aae5f7e..413fa89673e7df 100644
@@ -709,7 +709,7 @@ static struct qfq_class *qfq_classify(struct sk_buff *skb, struct Qdisc *sch,
 
 	*qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
 	fl = rcu_dereference_bh(q->filter_list);
-	result = tcf_classify(skb, NULL, fl, &res, false);
+	result = tcf_classify_qdisc(skb, fl, &res, false);
 	if (result >= 0) {
 #ifdef CONFIG_NET_CLS_ACT
 		switch (result) {
net/sched/sch_sfb.c +1 -1
index efd9251c3add31..ea1998a21d3b2b 100644
@@ -260,7 +260,7 @@ static bool sfb_classify(struct sk_buff *skb, struct tcf_proto *fl,
 	struct tcf_result res;
 	int result;
 
-	result = tcf_classify(skb, NULL, fl, &res, false);
+	result = tcf_classify_qdisc(skb, fl, &res, false);
 	if (result >= 0) {
 #ifdef CONFIG_NET_CLS_ACT
 		switch (result) {
net/sched/sch_sfq.c +1 -1
index f9807ee2cf6c72..04c8c2cae26bc5 100644
@@ -171,7 +171,7 @@ static unsigned int sfq_classify(struct sk_buff *skb, struct Qdisc *sch,
 		return sfq_hash(q, skb) + 1;
 
 	*qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
-	result = tcf_classify(skb, NULL, fl, &res, false);
+	result = tcf_classify_qdisc(skb, fl, &res, false);
 	if (result >= 0) {
 #ifdef CONFIG_NET_CLS_ACT
 		switch (result) {
Signature · 1 line
2.53.0
author maintainer
#235 lore raw

PATCH 235/744 bonding: fix devconf_all NULL dereference when IPv6 is disabled

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Zhaolong Zhang <[email protected]>

[ Upstream commit 1c975de3343cdef506f2eecc833cc1f14b0401c4 ]

When booting with the 'ipv6.disable=1' parameter, the devconf_all is never initialized because inet6_init() exits before addrconf_init() is called which initializes it. bond_send_validate(), however, will still call bond_ns_send_all() even ipv6 is indeed disabled. It will lead to NULL derefence of net->ipv6.devconf_all in ip6_pol_route().

 BUG: kernel NULL pointer dereference, address: 000000000000000c
 [...]
 Workqueue: bond0 bond_arp_monitor [bonding]
 RIP: 0010:ip6_pol_route+0x69/0x480
 [...]
 Call Trace:
  <TASK>
  ? srso_return_thunk+0x5/0x5f
  ? __pfx_ip6_pol_route_output+0x10/0x10
  fib6_rule_lookup+0xfe/0x260
  ? wakeup_preempt+0x8a/0x90
  ? srso_return_thunk+0x5/0x5f
  ? srso_return_thunk+0x5/0x5f
  ? sched_balance_rq+0x369/0x810
  ip6_route_output_flags+0xd7/0x170
  bond_ns_send_all+0xde/0x280 [bonding]
  bond_ab_arp_probe+0x296/0x320 [bonding]
  ? srso_return_thunk+0x5/0x5f
  bond_activebackup_arp_mon+0xb4/0x2c0 [bonding]
  process_one_work+0x196/0x370
  worker_thread+0x1af/0x320
  ? srso_return_thunk+0x5/0x5f
  ? __pfx_worker_thread+0x10/0x10
  kthread+0xe3/0x120
  ? __pfx_kthread+0x10/0x10
  ret_from_fork+0x199/0x260
  ? __pfx_kthread+0x10/0x10
  ret_from_fork_asm+0x1a/0x30
  </TASK>

Fix this by adding ipv6_mod_enabled() condition check in the caller.

---
 drivers/net/bonding/bond_main.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/net/bonding/bond_main.c +2 -1
index cd9b0a6d652174..75abd6d9af3a4e 100644
@@ -3446,7 +3446,8 @@ static void bond_send_validate(struct bonding *bond, struct slave *slave)
 {
 	bond_arp_send_all(bond, slave);
 #if IS_ENABLED(CONFIG_IPV6)
-	bond_ns_send_all(bond, slave);
+	if (likely(ipv6_mod_enabled()))
+		bond_ns_send_all(bond, slave);
 #endif
 }
 
Signature · 1 line
2.53.0
author maintainer
#236 lore raw

PATCH 236/744 rds: drop incoming messages that cross network namespace boundaries

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Aldo Ariel Panzardo <[email protected]>

[ Upstream commit 5521ae71e32a8069ed4ca6e792179dc57bc43ab2 ]

rds_find_bound() looks up the destination socket using a global rhashtable keyed solely on (addr, port, scope_id). Network namespaces are not part of the key, so a sender in netns A can deliver an incoming message (inc) to a socket that lives in a different netns B.

When this happens, inc->i_conn points to an rds_connection whose c_net is netns A, but the receiving rs lives in netns B. Once the child process that created netns A exits, cleanup_net() calls rds_loop_exit_net() -> rds_loop_kill_conns() -> rds_conn_destroy(), freeing that connection. If the survivor socket in netns B still holds the inc, any subsequent dereference of inc->i_conn is a use-after-free.

There are two dangerous sites in rds_clear_recv_queue():
1. inc->i_conn->c_lcong (offset 88 of freed rds_connection, size 200) read via rds_recv_rcvbuf_delta() -- confirmed by KASAN.
2. inc->i_conn->c_trans->inc_free(inc) (function pointer at offset 80) called via rds_inc_put() when the inc refcount reaches zero -- same race window, potential call-through-freed-object primitive.

The bug is reachable from unprivileged user namespaces (CLONE_NEWUSER + CLONE_NEWNET), available since Linux 3.8.

Fix this by rejecting the delivery in rds_recv_incoming() when the socket returned by rds_find_bound() belongs to a different network namespace than the connection that carried the message. Use the existing rds_conn_net() / sock_net() helpers and net_eq() for the comparison.

---
 net/rds/recv.c | 15 +++++++++++++++
 1 file changed, 15 insertions(+)
net/rds/recv.c +15 -0
index 4b3f9e4a8bfda5..cf3884d8793195 100644
@@ -399,6 +399,21 @@ void rds_recv_incoming(struct rds_connection *conn, struct in6_addr *saddr,
 		goto out;
 	}
 
+	/*
+	 * rds_find_bound() uses a global (netns-agnostic) hash table.
+	 * An RDS connection created in netns A can match a socket bound
+	 * in the init netns, delivering inc cross-netns with inc->i_conn
+	 * pointing into netns A.  When cleanup_net() then frees that conn,
+	 * any subsequent dereference of inc->i_conn is a use-after-free.
+	 * Drop the inc if the receiving socket lives in a different netns.
+	 */
+	if (!net_eq(sock_net(rds_rs_to_sk(rs)), rds_conn_net(conn))) {
+		rds_stats_inc(s_recv_drop_no_sock);
+		rds_sock_put(rs);
+		rs = NULL;
+		goto out;
+	}
+
 	/* Process extension headers */
 	rds_recv_incoming_exthdrs(inc, rs);
 
Signature · 1 line
2.53.0
author maintainer
#237 lore raw

PATCH 237/744 gtp: parse extension headers before reading inner protocol

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Zhixing Chen <[email protected]>

[ Upstream commit 96e37e2f618e931aa97af95e707dcdfb1ec41264 ]

GTPv1-U packets may carry a chain of extension headers before the inner IP packet. The receive path already parses and skips these extension headers, but it currently reads the inner protocol before doing so.

As a result, the first extension header byte is interpreted as the inner IP version. Packets with extension headers are then dropped before PDP lookup.

Parse the extension header chain before calling gtp_inner_proto(), so the inner protocol is read from the actual inner IP header.

Fixes c75fc0b9e5be ("gtp: identify tunnel via GTP device + GTP version + TEID + family")
---
 drivers/net/gtp.c | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)
drivers/net/gtp.c +6 -6
index a60ef32b35b825..c0e38878af51b3 100644
@@ -826,13 +826,17 @@ static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb)
 	if (!pskb_may_pull(skb, hdrlen))
 		return -1;
 
+	gtp1 = (struct gtp1_header *)(skb->data + sizeof(struct udphdr));
+
+	if (gtp1->flags & GTP1_F_EXTHDR &&
+	    gtp_parse_exthdrs(skb, &hdrlen) < 0)
+		return -1;
+
 	if (gtp_inner_proto(skb, hdrlen, &inner_proto) < 0) {
 		netdev_dbg(gtp->dev, "GTP packet does not encapsulate an IP packet\n");
 		return -1;
 	}
 
-	gtp1 = (struct gtp1_header *)(skb->data + sizeof(struct udphdr));
-
 	pctx = gtp1_pdp_find(gtp, ntohl(gtp1->tid),
 			     gtp_proto_to_family(inner_proto));
 	if (!pctx) {
@@ -840,10 +844,6 @@ static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb)
 		return 1;
 	}
 
-	if (gtp1->flags & GTP1_F_EXTHDR &&
-	    gtp_parse_exthdrs(skb, &hdrlen) < 0)
-		return -1;
-
 	return gtp_rx(pctx, skb, hdrlen, gtp->role, inner_proto);
 }
 
Signature · 1 line
2.53.0
author maintainer
#238 lore raw

PATCH 238/744 rxrpc: fix io_thread race in rxrpc_wake_up_io_thread()

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Xuanqiang Luo <[email protected]>

[ Upstream commit 745fb794c3e933c023af9dbb5876a5e16ad2dc71 ]

rxrpc_wake_up_io_thread() checks local->io_thread before waking it, but then reloads the pointer for wake_up_process().

local->io_thread is cleared with WRITE_ONCE() when the I/O thread exits, so the second load can see NULL even if the first load did not.

Take a READ_ONCE() snapshot and use it for both the NULL check and the wake_up_process() call, as rxrpc_encap_rcv() already does.

Fixes 5800b1cf3fd8 ("rxrpc: Allow CHALLENGEs to the passed to the app for a RESPONSE")
---
 net/rxrpc/ar-internal.h | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)
net/rxrpc/ar-internal.h +4 -2
index d2b31d15851b96..38b0997ce0cb3a 100644
@@ -1266,9 +1266,11 @@ int rxrpc_io_thread(void *data);
 void rxrpc_post_response(struct rxrpc_connection *conn, struct sk_buff *skb);
 static inline void rxrpc_wake_up_io_thread(struct rxrpc_local *local)
 {
-	if (!local->io_thread)
+	struct task_struct *io_thread = READ_ONCE(local->io_thread);
+
+	if (!io_thread)
 		return;
-	wake_up_process(READ_ONCE(local->io_thread));
+	wake_up_process(io_thread);
 }
 
 static inline bool rxrpc_protocol_error(struct sk_buff *skb, enum rxrpc_abort_reason why)
Signature · 1 line
2.53.0
author maintainer
#239 lore raw

PATCH 239/744 dpaa2-switch: put MAC endpoint device on disconnect

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Guangshuo Li <[email protected]>

[ Upstream commit 4c1eabbef7a1707635652e956e39db1269c3af2b ]

fsl_mc_get_endpoint() returns the MAC endpoint device with a reference taken through device_find_child(). The switch port connect path stores that device in mac->mc_dev and keeps it for the lifetime of the connected MAC object.

However, the disconnect path only closes the MAC and frees the dpaa2_mac object. It does not drop the endpoint device reference stored in mac->mc_dev, so every successful connect leaks that device reference when the MAC is later disconnected.

Drop the endpoint device reference before freeing the dpaa2_mac object.

Fixes 84cba72956fd ("dpaa2-switch: integrate the MAC endpoint support")
---
 drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c | 1 +
 1 file changed, 1 insertion(+)
drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c +1 -0
index fa4e4f47978234..9f465033331d31 100644
@@ -1511,6 +1511,7 @@ static void dpaa2_switch_port_disconnect_mac(struct ethsw_port_priv *port_priv)
 		dpaa2_mac_disconnect(mac);
 
 	dpaa2_mac_close(mac);
+	put_device(&mac->mc_dev->dev);
 	kfree(mac);
 }
 
Signature · 1 line
2.53.0
author maintainer
#240 lore raw

PATCH 240/744 net: airoha: Fix potential use-after-free in airoha_ppe_deinit()

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Wayen Yan <[email protected]>

[ Upstream commit 2484568a335cd7bda951c75b3a7d95ea36161ae7 ]

airoha_ppe_deinit() replaces the NPU pointer with NULL via rcu_replace_pointer() but does not wait for existing RCU readers to exit before calling ppe_deinit() and airoha_npu_put(). This can cause a use-after-free if a reader in an RCU read-side critical section still holds a reference to the NPU when it is freed.

The init path (airoha_ppe_init) already calls synchronize_rcu() after rcu_assign_pointer(), but the deinit path introduced in commit 6abcf751bc08 ("net: airoha: Fix schedule while atomic in airoha_ppe_deinit()") omitted the matching barrier when switching from rcu_read_lock()/rcu_dereference() to rcu_replace_pointer().

Add synchronize_rcu() before ppe_deinit() to ensure all existing RCU readers have completed before the NPU resources are released.

Fixes 6abcf751bc084804a9e5b3051442e8a2ce67f48a ("net: airoha: Fix schedule while atomic in airoha_ppe_deinit()")
---
 drivers/net/ethernet/airoha/airoha_ppe.c | 1 +
 1 file changed, 1 insertion(+)
drivers/net/ethernet/airoha/airoha_ppe.c +1 -0
index a1f9939c21f20a..75b33e5af9b2c5 100644
@@ -1619,6 +1619,7 @@ void airoha_ppe_deinit(struct airoha_eth *eth)
 	npu = rcu_replace_pointer(eth->npu, NULL,
 				  lockdep_is_held(&flow_offload_mutex));
 	if (npu) {
+		synchronize_rcu();
 		npu->ops.ppe_deinit(npu);
 		airoha_npu_put(npu);
 	}
Signature · 1 line
2.53.0
author maintainer
#241 lore raw

PATCH 241/744 dpaa2-eth: put MAC endpoint device on disconnect

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Guangshuo Li <[email protected]>

[ Upstream commit b4b201cc93ff70150853aba03e14d314d1980ca0 ]

fsl_mc_get_endpoint() returns the MAC endpoint device with a reference taken through device_find_child(). The Ethernet connect path stores that device in mac->mc_dev and keeps it for the lifetime of the connected MAC object.

However, the disconnect path only disconnects and closes the MAC before freeing the dpaa2_mac object. It does not drop the endpoint device reference stored in mac->mc_dev, so every successful connect leaks that device reference when the MAC is later disconnected.

Drop the endpoint device reference after closing the MAC and before freeing the dpaa2_mac object.

---
 drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c | 1 +
 1 file changed, 1 insertion(+)
drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c +1 -0
index 9335703768a9b6..764d2a09668f56 100644
@@ -4732,6 +4732,7 @@ static void dpaa2_eth_disconnect_mac(struct dpaa2_eth_priv *priv)
 		dpaa2_mac_disconnect(mac);
 
 	dpaa2_mac_close(mac);
+	put_device(&mac->mc_dev->dev);
 	kfree(mac);
 }
 
Signature · 1 line
2.53.0
author maintainer
#242 lore raw

PATCH 242/744 net: airoha: Fix DMA direction for NPU mailbox buffer

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Wayen Yan <[email protected]>

[ Upstream commit 6f884eb87a79e0c482baef2ad96c96b81d024235 ]

airoha_npu_send_msg() always maps the mailbox buffer with DMA_TO_DEVICE, but some callers expect the NPU to write response data back into the same buffer:

- airoha_npu_wlan_msg_get() (NPU_OP_GET): NPU writes response into the buffer, then the caller reads it via memcpy()
- airoha_npu_ppe_stats_setup() (NPU_OP_SET): NPU writes back npu_stats_addr field in the response

On non-cache-coherent architectures like EN7581 (Cortex-A53 without hardware cache coherency for NPU DMA), DMA_TO_DEVICE unmap is a no-op — it does not invalidate the CPU cache. If the NPU-written cache line is still present in the CPU cache when the caller reads the buffer, the CPU observes stale data instead of the NPU response.

This is a timing-sensitive bug: small mailbox buffers (~24 bytes) typically fit in a single cache line and may survive in the cache until the caller reads them, producing silent data corruption rather than a crash. The bug is more likely to trigger when the caller reads the response immediately after dma_unmap_single() without intervening cache-evicting operations.

Fix by using DMA_BIDIRECTIONAL for both map and unmap, which ensures dma_unmap_single() invalidates the CPU cache on non-coherent systems. The mailbox buffers are small so there is no performance concern.

Fixes c52918744ee1e49cea86622a2633b9782446428f ("net: airoha: npu: Move memory allocation in airoha_npu_send_msg() caller")
---
 drivers/net/ethernet/airoha/airoha_npu.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/net/ethernet/airoha/airoha_npu.c +2 -2
index 17dbdc8325336b..eab3eb4a3ab216 100644
@@ -168,7 +168,7 @@ static int airoha_npu_send_msg(struct airoha_npu *npu, int func_id,
 	dma_addr_t dma_addr;
 	int ret;
 
-	dma_addr = dma_map_single(npu->dev, p, size, DMA_TO_DEVICE);
+	dma_addr = dma_map_single(npu->dev, p, size, DMA_BIDIRECTIONAL);
 	ret = dma_mapping_error(npu->dev, dma_addr);
 	if (ret)
 		return ret;
@@ -191,7 +191,7 @@ static int airoha_npu_send_msg(struct airoha_npu *npu, int func_id,
 
 	spin_unlock_bh(&npu->cores[core].lock);
 
-	dma_unmap_single(npu->dev, dma_addr, size, DMA_TO_DEVICE);
+	dma_unmap_single(npu->dev, dma_addr, size, DMA_BIDIRECTIONAL);
 
 	return ret;
 }
Signature · 1 line
2.53.0
author maintainer
#243 lore raw

PATCH 243/744 iommu/amd: Wait for completion instead of returning early in iommu_completion_wait()

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Guanghui Feng <[email protected]>

[ Upstream commit 1e75a8255f11c81fb07e81e5029cfd75804350a0 ]

need_sync is a per-IOMMU flag shared by all domains and devices behind that IOMMU. It is set whenever a command is queued with sync == true and cleared when a completion-wait (CWAIT) command is queued. However, a cleared need_sync only means that a covering CWAIT has been queued, not that all previously queued commands have actually completed in hardware.

iommu_completion_wait() read need_sync locklessly and returned early when it was false. This breaks the "block until all previously queued commands have completed" contract in a multi-CPU scenario:

CPU2: queue inv-B => need_sync = true
CPU1: queue CWAIT(N); need_sync = false; then wait_on_sem(N)
CPU2: read need_sync == false => return 0 (no wait!)

CPU2 returns without waiting for any sequence number even though its inv-B may not have completed yet (CWAIT(N), queued after inv-B, has not been signaled). CPU2 then proceeds to, for example, free page-table pages while the IOMMU can still walk stale translations, opening a use-after-free window. This is a logical race in the meaning of the flag, not a memory-visibility issue, so barriers alone do not help.

Fix it without losing the optimization of avoiding redundant CWAIT
commands: take iommu->lock before testing need_sync, and when it is false do not return early but wait for the last allocated sequence number (cmd_sem_val). Since need_sync == false implies no sync command was queued after the last CWAIT, that CWAIT is FIFO-ordered after every not-yet-completed command, so waiting for its sequence number guarantees all prior commands (possibly queued by another CPU) have completed. The common path with pending work is unchanged and no extra hardware command is issued.

Fixes 815b33fdc279 ("x86/amd-iommu: Cleanup completion-wait handling")
---
 drivers/iommu/amd/iommu.c | 22 ++++++++++++++++------
 1 file changed, 16 insertions(+), 6 deletions(-)
drivers/iommu/amd/iommu.c +16 -6
index 15ffc4742183ea..345178a285c952 100644
@@ -1456,11 +1456,23 @@ static int iommu_completion_wait(struct amd_iommu *iommu)
 	int ret;
 	u64 data;
 
-	if (!iommu->need_sync)
-		return 0;
-
 	raw_spin_lock_irqsave(&iommu->lock, flags);
 
+	if (!iommu->need_sync) {
+		/*
+		 * No command has been queued since the last completion-wait.
+		 * A concurrent CPU may have already queued that CWAIT and
+		 * cleared need_sync; need_sync == false only means a covering
+		 * CWAIT is queued, not that all prior commands have completed.
+		 * Wait for the last allocated sequence number so that any
+		 * command queued before this call (possibly on another CPU)
+		 * is guaranteed to have completed before returning.
+		 */
+		data = iommu->cmd_sem_val;
+		raw_spin_unlock_irqrestore(&iommu->lock, flags);
+		return wait_on_sem(iommu, data);
+	}
+
 	data = get_cmdsem_val(iommu);
 	build_completion_wait(&cmd, iommu, data);
 
@@ -1470,9 +1482,7 @@ static int iommu_completion_wait(struct amd_iommu *iommu)
 	if (ret)
 		return ret;
 
-	ret = wait_on_sem(iommu, data);
-
-	return ret;
+	return wait_on_sem(iommu, data);
 }
 
 static void domain_flush_complete(struct protection_domain *domain)
Signature · 1 line
2.53.0
author maintainer
#244 lore raw

PATCH 244/744 wifi: mac80211: tear down new links on vif update error path

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Xiang Mei <[email protected]>

[ Upstream commit 952c02b33f56207a160421bcd61e7ac53c9c59ae ]

When ieee80211_vif_update_links() adds new links it allocates a link container for each and calls ieee80211_link_init() (which registers the per-link debugfs files with file->private_data pointing into the container) and ieee80211_link_setup(). If the subsequent drv_change_vif_links() fails, the error path restores the old pointers and jumps to 'free', which frees the new containers but never removes their debugfs entries or stops the links. The debugfs files survive with file->private_data dangling at the freed container, so a later open()+read() (e.g. link-1/txpower) dereferences freed memory in ieee80211_if_read_link(), a use-after-free.

The removal path already dismantles links correctly via ieee80211_tear_down_links(), which removes each link's keys and debugfs entries and calls ieee80211_link_stop(); the add path on the error branch does not. Commit be1ba9ed221f ("wifi: mac80211: avoid weird state in error path") hardened this same error path for the link-removal case (new_links == 0) but left the newly-added links' teardown unaddressed.

drv_change_vif_links() can fail at runtime on MLO drivers (internal allocation / queue / firmware command failures).

Remove the new links' debugfs entries and stop them before freeing.

  BUG: KASAN: slab-use-after-free in ieee80211_if_read_link (net/mac80211/debugfs_netdev.c:127)
  Read of size 8 at addr ffff888011290000 by task exploit/145
  Call Trace:
   ...
   ieee80211_if_read_link (net/mac80211/debugfs_netdev.c:127)
   short_proxy_read (fs/debugfs/file.c:373)
   vfs_read (fs/read_write.c:572)
   ksys_read (fs/read_write.c:716)
   do_syscall_64 (arch/x86/entry/syscall_64.c:94)
   entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121)
  ...
  Oops: general protection fault, probably for non-canonical address 0xdffffc000000000a
  RIP: 0010:ieee80211_if_read_link (net/mac80211/debugfs_netdev.c:127)
  Kernel panic - not syncing: Fatal exception
Fixes 170cd6a66d9a ("wifi: mac80211: add netdev per-link debugfs data and driver hook")
Assisted-by Claude:claude-opus-4-8
---
 net/mac80211/link.c | 4 ++++
 1 file changed, 4 insertions(+)
net/mac80211/link.c +4 -0
index 93e290dd783f2b..7e3dae12526b08 100644
@@ -394,6 +394,10 @@ static int ieee80211_vif_update_links(struct ieee80211_sub_if_data *sdata,
 		memcpy(sdata->link, old_data, sizeof(old_data));
 		memcpy(sdata->vif.link_conf, old, sizeof(old));
 		ieee80211_set_vif_links_bitmaps(sdata, old_links, dormant_links);
+		for_each_set_bit(link_id, &add, IEEE80211_MLD_MAX_NUM_LINKS) {
+			ieee80211_link_debugfs_remove(&links[link_id]->data);
+			ieee80211_link_stop(&links[link_id]->data);
+		}
 		/* and free (only) the newly allocated links */
 		memset(to_free, 0, sizeof(links));
 		goto free;
Signature · 1 line
2.53.0
author maintainer
#245 lore raw

PATCH 245/744 nfp: Check resource mutex allocation

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Ruoyu Wang <[email protected]>

[ Upstream commit a61b4db34a753bdf5c9e77a7f3d3dddd41dcfacc ]

nfp_cpp_resource_find() allocates a CPP mutex handle for the matching resource-table entry and then reports success. nfp_resource_try_acquire() immediately passes that handle to nfp_cpp_mutex_trylock().

However, nfp_cpp_mutex_alloc() returns NULL on failure. If that happens for a matching table entry, the resource lookup still returns success and the following trylock dereferences a NULL mutex pointer while opening the resource.

nfp_resource_acquire() already treats failure to allocate the table mutex as -ENOMEM. Do the same for the resource mutex and fail the lookup before publishing the rest of the resource handle.

This issue was found by a static analysis checker and confirmed by manual source review.

---
 drivers/net/ethernet/netronome/nfp/nfpcore/nfp_resource.c | 3 +++
 1 file changed, 3 insertions(+)
drivers/net/ethernet/netronome/nfp/nfpcore/nfp_resource.c +3 -0
index 6d5833479d123f..237300b82b9132 100644
@@ -96,6 +96,9 @@ static int nfp_cpp_resource_find(struct nfp_cpp *cpp, struct nfp_resource *res)
 		res->mutex =
 			nfp_cpp_mutex_alloc(cpp,
 					    NFP_RESOURCE_TBL_TARGET, addr, key);
+		if (!res->mutex)
+			return -ENOMEM;
+
 		res->cpp_id = NFP_CPP_ID(entry.region.cpp_target,
 					 entry.region.cpp_action,
 					 entry.region.cpp_token);
Signature · 1 line
2.53.0
author maintainer
#246 lore raw

PATCH 246/744 wan: wanxl: Only reset hardware after BAR mapping

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Ruoyu Wang <[email protected]>

[ Upstream commit 91957b89da995607cb654b1f9a3c126ddbaee10f ]

wanxl_pci_init_one() stores the freshly allocated card in driver data before the PLX BAR is mapped. Several early probe failures then unwind through wanxl_pci_remove_one(), including failure to allocate the coherent status area or to restore the DMA mask.

wanxl_pci_remove_one() unconditionally calls wanxl_reset(), and wanxl_reset() dereferences card->plx. On those early failures card->plx is still NULL, so the error path can dereference a NULL MMIO pointer.

Only issue the hardware reset once the BAR mapping exists. The remaining cleanup in wanxl_pci_remove_one() already checks whether later resources were allocated.

This issue was found by a static analysis checker and confirmed by manual source review.

---
 drivers/net/wan/wanxl.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/net/wan/wanxl.c +2 -1
index 3f770711845053..8790251feb7d6d 100644
@@ -514,7 +514,8 @@ static void wanxl_pci_remove_one(struct pci_dev *pdev)
 	if (card->irq)
 		free_irq(card->irq, card);
 
-	wanxl_reset(card);
+	if (card->plx)
+		wanxl_reset(card);
 
 	for (i = 0; i < RX_QUEUE_LENGTH; i++)
 		if (card->rx_skbs[i]) {
Signature · 1 line
2.53.0
author maintainer
#247 lore raw

PATCH 247/744 vhost-net: fix TX stall when vhost owns virtio-net header

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Enrico Zanda <[email protected]>

[ Upstream commit 3c0d10f233f19153f81fef685b5c6716776a5af3 ]

When vhost owns the virtio-net header, i.e. when VHOST_NET_F_VIRTIO_NET_HDR is negotiated, sock_hlen is 0, meaning that no header will be forwarded to the TAP device.

In the current vhost_net_build_xdp() implementation,
when sock_hlen == 0, the gso pointer can point at the start of the Ethernet frame instead of a virtio-net header.
This results in a wrong interpretation of the destination MAC address bytes as struct virtio_net_hdr fields.

This can, for some MAC addresses, trigger -EINVAL and return early before the TX descriptor is completed, which can stall vhost-net TX.

Before 97b2409f28e0, the gso pointer was set to the zeroed padding area, using it as a synthetic virtio-net header. Restore that behavior.

Fixes 97b2409f28e0 ("vhost-net: reduce one userspace copy when building XDP buff")
---
 drivers/vhost/net.c | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)
drivers/vhost/net.c +5 -3
index b9af63fb630602..6949b704166d58 100644
@@ -722,10 +722,12 @@ static int vhost_net_build_xdp(struct vhost_net_virtqueue *nvq,
 		goto err;
 	}
 
-	gso = buf + pad - sock_hlen;
-
-	if (!sock_hlen)
+	if (!sock_hlen) {
 		memset(buf, 0, pad);
+		gso = buf;
+	} else {
+		gso = buf + pad - sock_hlen;
+	}
 
 	if ((gso->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) &&
 	    vhost16_to_cpu(vq, gso->csum_start) +
Signature · 1 line
2.53.0
author maintainer
#248 lore raw

PATCH 248/744 wifi: mac80211: copy aggregation information

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Benjamin Berg <[email protected]>

[ Upstream commit 0fe2d5be7ab59717adb3f9cfab3832c6c4dd770c ]

This information can be considered part of the capabilities and should also be copied to the NAN data station.

---
 net/mac80211/cfg.c | 3 +++
 1 file changed, 3 insertions(+)
net/mac80211/cfg.c +3 -0
index 0bc10136b816ef..80210ef4cbc843 100644
@@ -2414,6 +2414,9 @@ static int sta_apply_parameters(struct ieee80211_local *local,
 		memcpy(&sta->deflink.pub->supp_rates,
 		       &nmi_sta->deflink.pub->supp_rates,
 		       sizeof(sta->deflink.pub->supp_rates));
+
+		sta->deflink.pub->agg = nmi_sta->deflink.pub->agg;
+		__ieee80211_sta_recalc_aggregates(sta, 0);
 	}
 
 	/* set the STA state after all sta info from usermode has been set */
Signature · 1 line
2.53.0
author maintainer
#249 lore raw

PATCH 249/744 wifi: mwifiex: bound uAP association event IEs to the event buffer

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: HE WEI (ギカク) <[email protected]>

[ Upstream commit f0858bfc7d3cab411a447b88e3ef970e575032c9 ]

mwifiex_process_uap_event() handles EVENT_UAP_STA_ASSOC by exposing the (re)association request IEs that the firmware copies into the event:

	sinfo->assoc_req_ies = &event->data[len];
	len = (u8 *)sinfo->assoc_req_ies - (u8 *)&event->frame_control;
	sinfo->assoc_req_ies_len = le16_to_cpu(event->len) - (u16)len;

event->len is supplied by the device firmware and is never validated, and the subtraction is unchecked. assoc_req_ies points into adapter->event_body[MAX_EVENT_SIZE], a fixed-size array embedded in the kmalloc()'d struct mwifiex_adapter.

On the ap_11n_enabled path mwifiex_set_sta_ht_cap() walks these IEs with cfg80211_find_ie(), whose for_each_element() loop dereferences each element header. A firmware-reported event->len larger than the bytes actually received makes assoc_req_ies_len describe IEs that extend past event_body, so the walk reads out of the adapter slab object, a slab-out-of-bounds read (KASAN: slab-out-of-bounds in cfg80211_find_ie). An event->len smaller than the header instead makes the int subtraction negative, which wraps to a huge size_t when stored in assoc_req_ies_len. The same length is handed to cfg80211_new_sta(), so a more modest over-claim can also copy stale event_body bytes into the NL80211_CMD_NEW_STATION notification.

A malicious or malfunctioning mwifiex device (USB/SDIO/PCIe) can deliver such an event while the interface is in AP/uAP mode.

Validate event->len before use: reject a length that underflows the header or that would place the IEs outside the event_body[] buffer the event was copied into. event->len here is struct mwifiex_assoc_event.len, a payload field internal to this event, not the transport frame length, so it is validated in this handler rather than at the generic MWIFIEX_TYPE_EVENT receive path, which only sees the event cause and the transport frame length. The bound is against event_body[MAX_EVENT_SIZE] rather than the actually-received length because the transports store the event differently (USB and SDIO leave the 4-byte event header in event_skb, PCIe strips it via skb_pull), whereas event_body is the single fixed buffer all of them copy the event into. This is the event-path analogue of the receive-path bounds checks added in commit 119585281617 ("wifi: mwifiex: Fix OOB and integer underflow when rx packets").

---
 .../net/wireless/marvell/mwifiex/uap_event.c  | 24 +++++++++++++++++--
 1 file changed, 22 insertions(+), 2 deletions(-)
drivers/net/wireless/marvell/mwifiex/uap_event.c +22 -2
index 679fdae0f001a1..ba1bdbbff687be 100644
@@ -123,11 +123,31 @@ int mwifiex_process_uap_event(struct mwifiex_private *priv)
 				len = ETH_ALEN;
 
 			if (len != -1) {
+				u16 evt_len = le16_to_cpu(event->len);
+
 				sinfo->assoc_req_ies = &event->data[len];
 				len = (u8 *)sinfo->assoc_req_ies -
 				      (u8 *)&event->frame_control;
-				sinfo->assoc_req_ies_len =
-					le16_to_cpu(event->len) - (u16)len;
+
+				/*
+				 * event->len is reported by the device firmware
+				 * and is not otherwise validated.  Reject a
+				 * length that underflows the header, or that
+				 * would place the association request IEs
+				 * outside the fixed-size event_body[] buffer the
+				 * event was copied into; otherwise the IE walk
+				 * in mwifiex_set_sta_ht_cap() reads past
+				 * event_body and out of the adapter slab object.
+				 */
+				if (evt_len < len ||
+				    (u8 *)&event->frame_control + evt_len >
+				    adapter->event_body + MAX_EVENT_SIZE) {
+					mwifiex_dbg(adapter, ERROR,
+						    "invalid STA assoc event length\n");
+					kfree(sinfo);
+					return -1;
+				}
+				sinfo->assoc_req_ies_len = evt_len - (u16)len;
 			}
 		}
 		cfg80211_new_sta(priv->netdev->ieee80211_ptr, event->sta_addr,
Signature · 1 line
2.53.0
author maintainer
#250 lore raw

PATCH 250/744 iommu/amd: Bound the early ACPI HID map

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Pengpeng Hou <[email protected]>

[ Upstream commit fb80117fddb5b477218dc99bb53911b72c3847f8 ]

The ivrs_acpihid command-line parser appends entries to a fixed four-element early_acpihid_map array. Unlike the sibling IOAPIC and HPET parsers, it does not reject a fifth entry before incrementing the map size.

Check the capacity at the common found label before parsing the HID and UID or writing the entry.

Fixes ca3bf5d47cec ("iommu/amd: Introduces ivrs_acpihid kernel parameter")
---
 drivers/iommu/amd/init.c | 6 ++++++
 1 file changed, 6 insertions(+)
drivers/iommu/amd/init.c +6 -0
index 9a846dcd030638..0df711c082cb20 100644
@@ -3872,6 +3872,12 @@ static int __init parse_ivrs_acpihid(char *str)
 	return 1;
 
 found:
+	if (early_acpihid_map_size == EARLY_MAP_SIZE) {
+		pr_err("Early ACPI HID map overflow - ignoring ivrs_acpihid%s\n",
+		       str);
+		return 1;
+	}
+
 	p = acpiid;
 	hid = strsep(&p, ":");
 	uid = p;
Signature · 1 line
2.53.0
author maintainer
#251 lore raw

PATCH 251/744 iommu/intel: Fix out-of-bounds memset in dmar_latency_disable()

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Li RongQing <[email protected]>

[ Upstream commit 754f8efe45f87e3a9c6871b645b2f9d46d1b407b ]

dmar_latency_disable() intends to zero out only the single latency_statistic entry for the given type, but the memset size was computed as sizeof(*lstat) * DMAR_LATENCY_NUM, which clears the entire array starting from &lstat[type].

When type > 0, this writes beyond the end of the allocated array, corrupting adjacent memory.

Fix by using sizeof(*lstat) to clear only the target entry.

Fixes 55ee5e67a59a ("iommu/vt-d: Add common code for dmar latency performance monitors")
---
 drivers/iommu/intel/perf.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/iommu/intel/perf.c +1 -1
index 02168f2f20a43f..bec98dcbb9ecae 100644
@@ -63,7 +63,7 @@ void dmar_latency_disable(struct intel_iommu *iommu, enum latency_type type)
 		return;
 
 	spin_lock_irqsave(&latency_lock, flags);
-	memset(&lstat[type], 0, sizeof(*lstat) * DMAR_LATENCY_NUM);
+	memset(&lstat[type], 0, sizeof(*lstat));
 	spin_unlock_irqrestore(&latency_lock, flags);
 }
 
Signature · 1 line
2.53.0
author maintainer
#252 lore raw

PATCH 252/744 wifi: mac80211: recalculate TIM when a station enters power save

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Andrew Pope <[email protected]>

[ Upstream commit a007a384c9eb17610f53a53e2f59944c31f1565a ]

When an AP buffers frames for a station on its per-station TXQs and the station subsequently enters power save, sta_ps_start() records the buffered TIDs in txq_buffered_tids but does not update the TIM. The station's TIM bit is only ever set when a further frame is buffered while the station is already asleep (ieee80211_tx_h_unicast_ps_buf() -> sta_info_recalc_tim()).

If no further downlink frame arrives for that station the beacon TIM never advertises the buffered traffic. A station relying on the TIM then remains in doze indefinitely on top of a non-empty queue. Its TXQs were removed from the scheduler's active list at PS entry, nothing pages it, and the flow deadlocks until an unrelated event wakes the station.

Recalculate the TIM at the end of sta_ps_start(), so traffic already buffered at PS entry is advertised immediately. sta_info_recalc_tim() already consults txq_buffered_tids, which is updated above, and is safe in this context (it is already called from equivalent paths such as the tx handlers and ieee80211_handle_filtered_frame()).

Fixes ba8c3d6f16a1 ("mac80211: add an intermediate software queue implementation")
[add wifi: subject prefix]
---
 net/mac80211/rx.c | 2 ++
 1 file changed, 2 insertions(+)
net/mac80211/rx.c +2 -0
index fe583636d326eb..7372025831c5dc 100644
@@ -1717,6 +1717,8 @@ static void sta_ps_start(struct sta_info *sta)
 		else
 			clear_bit(tid, &sta->txq_buffered_tids);
 	}
+
+	sta_info_recalc_tim(sta);
 }
 
 static void sta_ps_end(struct sta_info *sta)
Signature · 1 line
2.53.0
author maintainer
#253 lore raw

PATCH 253/744 pds_core: reject component parameter in legacy firmware update

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Nikhil P. Rao <[email protected]>

[ Upstream commit 7be2552e601c247a328a5aba6fc06ac844b94a16 ]

The legacy firmware update path does not support per-component updates. If a user specifies a component parameter with devlink flash, reject the request with -EOPNOTSUPP rather than silently ignoring the component parameter and flashing the entire firmware image.

Fixes 49ce92fbee0b ("pds_core: add FW update feature to devlink")
---
 drivers/net/ethernet/amd/pds_core/devlink.c | 6 ++++++
 1 file changed, 6 insertions(+)
drivers/net/ethernet/amd/pds_core/devlink.c +6 -0
index 3f0e56b951bf0b..d6fccd89476a0a 100644
@@ -90,6 +90,12 @@ int pdsc_dl_flash_update(struct devlink *dl,
 {
 	struct pdsc *pdsc = devlink_priv(dl);
 
+	if (params->component) {
+		NL_SET_ERR_MSG_MOD(extack,
+				   "Component update not supported by this device");
+		return -EOPNOTSUPP;
+	}
+
 	return pdsc_firmware_update(pdsc, params->fw, extack);
 }
 
Signature · 1 line
2.53.0
author maintainer
#254 lore raw

PATCH 254/744 arm64: Correct value returned by ESR_ELx_FSC_ADDRSZ_nL()

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Steven Price <[email protected]>

[ Upstream commit b877075d0baa22c225842c2f19e3ea0a9cbcbe39 ]

Address size fault, level -1 is encoded as 0b101001 or 0x29 according to the Arm ARM. Correct the value to match the spec. This also matches the offset of "level -1 address size fault" in the fault_info array in fault.c.

Fixes fb8a3eba9c81 ("KVM: arm64: Only read HPFAR_EL2 when value is architecturally valid")
---
 arch/arm64/include/asm/esr.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
arch/arm64/include/asm/esr.h +1 -1
index 7e86d400864e03..64e9bd137b603c 100644
@@ -131,7 +131,7 @@
  * Annoyingly, the negative levels for Address size faults aren't laid out
  * contiguously (or in the desired order)
  */
-#define ESR_ELx_FSC_ADDRSZ_nL(n)	((n) == -1 ? 0x25 : 0x2C)
+#define ESR_ELx_FSC_ADDRSZ_nL(n)	((n) == -1 ? 0x29 : 0x2C)
 #define ESR_ELx_FSC_ADDRSZ_L(n)		((n) < 0 ? ESR_ELx_FSC_ADDRSZ_nL(n) : \
 						   (ESR_ELx_FSC_ADDRSZ + (n)))
 
Signature · 1 line
2.53.0
author maintainer
#255 lore raw

PATCH 255/744 arm64/mm: Check the requested PFN range during memory removal

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Richard Cheng <[email protected]>

[ Upstream commit 285f90a4d1141c7594f2368e19cbb307388eff30 ]

prevent_memory_remove_notifier() advances pfn while scanning the requested range for early memory. When the loop completes, pfn is at or beyond end_pfn. Passing it to can_unmap_without_split() therefore checks a range after the one being offlined.

Consequently, a valid request can be rejected based on the following range, while a request that would split a leaf mapping can be accepted if the shifted range can be unmapped without a split. This was observed with CXL DAX memory, where the final memory block was incorrectly allowed to be offlined.

Pass arg->start_pfn into can_unmap_without_split() so it checks the requested range.

Fixes 95a58852b0e5 ("arm64/mm: Reject memory removal that splits a kernel leaf mapping")
---
 arch/arm64/mm/mmu.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
arch/arm64/mm/mmu.c +1 -1
index fd756390fe925c..fbbd31cd4cbd84 100644
@@ -2201,7 +2201,7 @@ static int prevent_memory_remove_notifier(struct notifier_block *nb,
 		}
 	}
 
-	if (!can_unmap_without_split(pfn, arg->nr_pages))
+	if (!can_unmap_without_split(arg->start_pfn, arg->nr_pages))
 		return NOTIFY_BAD;
 
 	return NOTIFY_OK;
Signature · 1 line
2.53.0
author maintainer
#256 lore raw

PATCH 256/744 amd-xgbe: fix MAC_AUTO_SW handling in CL37 AN

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Prashanth Kumar KR <[email protected]>

[ Upstream commit 4bf22afe53a1de4b44b04cf677fd5199089cbdff ]

MAC_AUTO_SW (VR_MII_DIG_CTRL1 bit 9) enables automatic XPCS speed mode switching after CL37 auto-negotiation and is only meaningful in SGMII MAC mode. The original code unconditionally set this bit on every call to xgbe_an37_set(), including when called from xgbe_an37_disable() with enable=false. This left MAC_AUTO_SW=1 after AN was disabled, causing the XPCS to autonomously switch speed from stale AN state during subsequent mode changes, breaking SGMII speed negotiation on 1G copper SFP modules.

Patrick: This was breaking negotiation for all 1G SFP modules, not just copper modules.

---
 drivers/net/ethernet/amd/xgbe/xgbe-mdio.c | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)
drivers/net/ethernet/amd/xgbe/xgbe-mdio.c +8 -3
index fa0df61812076a..12770af031eb8b 100644
@@ -267,9 +267,14 @@ static void xgbe_an37_set(struct xgbe_prv_data *pdata, bool enable,
 
 	XMDIO_WRITE(pdata, MDIO_MMD_VEND2, MDIO_CTRL1, reg);
 
-	reg = XMDIO_READ(pdata, MDIO_MMD_VEND2, MDIO_PCS_DIG_CTRL);
-	reg |= XGBE_VEND2_MAC_AUTO_SW;
-	XMDIO_WRITE(pdata, MDIO_MMD_VEND2, MDIO_PCS_DIG_CTRL, reg);
+	if (pdata->an_mode == XGBE_AN_MODE_CL37_SGMII) {
+		reg = XMDIO_READ(pdata, MDIO_MMD_VEND2, MDIO_PCS_DIG_CTRL);
+		if (enable)
+			reg |= XGBE_VEND2_MAC_AUTO_SW;
+		else
+			reg &= ~XGBE_VEND2_MAC_AUTO_SW;
+		XMDIO_WRITE(pdata, MDIO_MMD_VEND2, MDIO_PCS_DIG_CTRL, reg);
+	}
 }
 
 static void xgbe_an37_restart(struct xgbe_prv_data *pdata)
Signature · 1 line
2.53.0
author maintainer
#257 lore raw

PATCH 257/744 soreuseport: Clear sk_reuseport_cb before failure in sk_clone().

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Kuniyuki Iwashima <[email protected]>

[ Upstream commit 98da8ce87dd561f08fbe44f75865edc5d9b2ba5f ]

When sk_clone() fails, sk_destruct() is called for the new socket.

If the parent socket has sk->sk_reuseport_cb, the child will call reuseport_detach_sock() for the reuseport group.

Let's clear sk->sk_reuseport_cb before any failure path in sk_clone().

Note that this was not a problem before the cited commit because reuseport_detach_sock() did nothing if the socket was not found in the reuseport array.

---
 net/core/sock.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
net/core/sock.c +2 -2
index 1c73b6841d0ae9..d0f518381334ca 100644
@@ -2547,6 +2547,8 @@ struct sock *sk_clone(const struct sock *sk, const gfp_t priority,
 
 	cgroup_sk_clone(&newsk->sk_cgrp_data);
 
+	RCU_INIT_POINTER(newsk->sk_reuseport_cb, NULL);
+
 	rcu_read_lock();
 	filter = rcu_dereference(sk->sk_filter);
 	if (filter != NULL)
@@ -2569,8 +2571,6 @@ struct sock *sk_clone(const struct sock *sk, const gfp_t priority,
 		goto free;
 	}
 
-	RCU_INIT_POINTER(newsk->sk_reuseport_cb, NULL);
-
 	if (bpf_sk_storage_clone(sk, newsk))
 		goto free;
 
Signature · 1 line
2.53.0
author maintainer
#258 lore raw

PATCH 258/744 net: Call net_enable_timestamp() before failure in sk_clone().

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Kuniyuki Iwashima <[email protected]>

[ Upstream commit d50557779257a00162411e3048d82971ff1f644c ]

When sk_clone() fails, sk_destruct() is called for the new socket.

If the parent socket has SK_FLAGS_TIMESTAMP in sk->sk_flags, net_disable_timestamp() is called for the child socket even though net_enable_timestamp() is not called for it.

Let's call net_enable_timestamp() before any failure path in sk_clone().

---
 net/core/sock.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)
net/core/sock.c +3 -3
index d0f518381334ca..5714436b087100 100644
@@ -2549,6 +2549,9 @@ struct sock *sk_clone(const struct sock *sk, const gfp_t priority,
 
 	RCU_INIT_POINTER(newsk->sk_reuseport_cb, NULL);
 
+	if (sock_needs_netstamp(sk) && newsk->sk_flags & SK_FLAGS_TIMESTAMP)
+		net_enable_timestamp();
+
 	rcu_read_lock();
 	filter = rcu_dereference(sk->sk_filter);
 	if (filter != NULL)
@@ -2598,9 +2601,6 @@ struct sock *sk_clone(const struct sock *sk, const gfp_t priority,
 
 	if (newsk->sk_prot->sockets_allocated)
 		sk_sockets_allocated_inc(newsk);
-
-	if (sock_needs_netstamp(sk) && newsk->sk_flags & SK_FLAGS_TIMESTAMP)
-		net_enable_timestamp();
 out:
 	return newsk;
 free:
Signature · 1 line
2.53.0
author maintainer
#259 lore raw

PATCH 259/744 geneve: fix hint header definition wrt endianness

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Antoine Tenart <[email protected]>

[ Upstream commit 751bfa982b4a6de8275a552804f6971adcf08473 ]

Bitfields are packed differently depending on the endianness, take it into account in the GRO hint header definition.

---
 drivers/net/geneve.c | 11 ++++++++++-
 1 file changed, 10 insertions(+), 1 deletion(-)
drivers/net/geneve.c +10 -1
index c55bdb805a644f..e9102548ac438d 100644
@@ -43,8 +43,17 @@ MODULE_PARM_DESC(log_ecn_error, "Log packets received with corrupted ECN");
 #define GENEVE_OPT_GRO_HINT_LEN		1
 
 struct geneve_opt_gro_hint {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
 	u8	inner_proto_id:2,
-		nested_is_v6:1;
+		nested_is_v6:1,
+		rsvd:5;
+#elif defined(__BIG_ENDIAN_BITFIELD)
+	u8	rsvd:5,
+		nested_is_v6:1,
+		inner_proto_id:2;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
 	u8	nested_nh_offset;
 	u8	nested_tp_offset;
 	u8	nested_hdr_len;
Signature · 1 line
2.53.0
author maintainer
#260 lore raw

PATCH 260/744 geneve: ensure the skb is writable before fixing its headers

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Antoine Tenart <[email protected]>

[ Upstream commit 447ec540233c60d6af4d68a164a5bc8ce7e975c1 ]

Make sure the IPv4/6 and UDP headers are writable before fixing them up in geneve_post_decap_hint. As skb_ensure_writable can reallocate the skb linear area, reload the GRO hint header pointer and only set the IPv4/6 header ones after the call.

---
 drivers/net/geneve.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)
drivers/net/geneve.c +11 -2
index e9102548ac438d..29041175b7b80c 100644
@@ -585,6 +585,7 @@ static int geneve_post_decap_hint(const struct sock *sk, struct sk_buff *skb,
 	struct iphdr *iph;
 	struct udphdr *uh;
 	__be16 p;
+	int err;
 
 	hint_off = geneve_sk_gro_hint_off(sk, *geneveh, &p, &len);
 	if (!hint_off)
@@ -609,12 +610,20 @@ static int geneve_post_decap_hint(const struct sock *sk, struct sk_buff *skb,
 		     !geneve_opt_gro_hint_validate(skb->data, gro_hint)))
 		return -EINVAL;
 
-	ipv6h = (void *)skb->data + gro_hint->nested_nh_offset;
-	iph = (struct iphdr *)ipv6h;
 	total_len = skb->len - gro_hint->nested_nh_offset;
 	if (total_len >= GRO_LEGACY_MAX_SIZE)
 		return -E2BIG;
 
+	err = skb_ensure_writable(skb, gro_hint->nested_tp_offset + sizeof(*uh));
+	if (unlikely(err))
+		return err;
+
+	*geneveh = geneve_hdr(skb);
+	gro_hint = geneve_opt_gro_hint(*geneveh, hint_off);
+
+	ipv6h = (void *)skb->data + gro_hint->nested_nh_offset;
+	iph = (struct iphdr *)ipv6h;
+
 	/*
 	 * After stripping the outer encap, the packet still carries a
 	 * tunnel encapsulation: the nested one.
Signature · 1 line
2.53.0
author maintainer
#261 lore raw

PATCH 261/744 tipc: serialize udp bearer replicast list updates

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Weiming Shi <[email protected]>

[ Upstream commit 350e592ff4e30e48ffb55e142d11a73e63f4869c ]

tipc_udp_rcast_add() and cleanup_bearer() both update ub->rcast.list with list_add_rcu() / list_del_rcu(), but nothing serializes them. The add runs from the encap receive softirq (via tipc_udp_rcast_disc()) without rtnl_lock(), so it can race the cleanup delete and corrupt the list:

list_del corruption. prev->next should be ffff8880298d7ab8,

    but was ffff88802449ad38. (prev=ffff888027e3ec98)
  kernel BUG at lib/list_debug.c:62!
  RIP: __list_del_entry_valid_or_report+0x17a/0x200
  Workqueue: events cleanup_bearer
  Call Trace:
   cleanup_bearer (net/tipc/udp_media.c:811)
   process_one_work (kernel/workqueue.c:3302)
   worker_thread (kernel/workqueue.c:3466)

The bearer can be enabled from an unprivileged user namespace, as the TIPCv2 generic-netlink ops carry no GENL_ADMIN_PERM.

Add a spinlock to struct udp_bearer and take it around the list_add_rcu() in tipc_udp_rcast_add() and the list_del_rcu() loop in cleanup_bearer() so the two writers can no longer corrupt the list.

Reject a duplicate peer under the same lock before allocating, and remove tipc_udp_is_known_peer(). The old lockless pre-check in tipc_udp_rcast_disc() was racy: two softirqs discovering the same peer could both find it absent and add it twice.

cleanup_bearer() runs from a workqueue after tipc_udp_disable() clears the bearer's up bit, so an encap softirq can still reach tipc_udp_rcast_add() and add a peer after cleanup_bearer() has already emptied the list, leaking that entry when the bearer is freed. Mark the bearer disabled under rcast_lock once the list is emptied and refuse further additions.

---
 net/tipc/udp_media.c | 56 +++++++++++++++++++++-----------------------
 1 file changed, 27 insertions(+), 29 deletions(-)
net/tipc/udp_media.c +27 -29
index 62ae7f5b58409c..230645cc01c921 100644
@@ -94,6 +94,8 @@ struct udp_replicast {
  * @ifindex:	local address scope
  * @work:	used to schedule deferred work on a bearer
  * @rcast:	associated udp_replicast container
+ * @rcast_lock:	serialize updates to @rcast.list against concurrent updaters
+ * @disabled:	bearer is being torn down; reject further @rcast.list additions
  */
 struct udp_bearer {
 	struct tipc_bearer __rcu *bearer;
@@ -101,6 +103,8 @@ struct udp_bearer {
 	u32 ifindex;
 	struct work_struct work;
 	struct udp_replicast rcast;
+	spinlock_t rcast_lock;
+	bool disabled;
 };
 
 static int tipc_udp_is_mcast_addr(struct udp_media_addr *addr)
@@ -278,26 +282,6 @@ static int tipc_udp_send_msg(struct net *net, struct sk_buff *skb,
 	return err;
 }
 
-static bool tipc_udp_is_known_peer(struct tipc_bearer *b,
-				   struct udp_media_addr *addr)
-{
-	struct udp_replicast *rcast, *tmp;
-	struct udp_bearer *ub;
-
-	ub = rcu_dereference_rtnl(b->media_ptr);
-	if (!ub) {
-		pr_err_ratelimited("UDP bearer instance not found\n");
-		return false;
-	}
-
-	list_for_each_entry_safe(rcast, tmp, &ub->rcast.list, list) {
-		if (!memcmp(&rcast->addr, addr, sizeof(struct udp_media_addr)))
-			return true;
-	}
-
-	return false;
-}
-
 static int tipc_udp_rcast_add(struct tipc_bearer *b,
 			      struct udp_media_addr *addr)
 {
@@ -308,16 +292,34 @@ static int tipc_udp_rcast_add(struct tipc_bearer *b,
 	if (!ub)
 		return -ENODEV;
 
+	spin_lock_bh(&ub->rcast_lock);
+	if (ub->disabled) {
+		spin_unlock_bh(&ub->rcast_lock);
+		return 0;
+	}
+	list_for_each_entry(rcast, &ub->rcast.list, list) {
+		if (!memcmp(&rcast->addr, addr, sizeof(*addr))) {
+			spin_unlock_bh(&ub->rcast_lock);
+			return 0;
+		}
+	}
+
 	rcast = kmalloc_obj(*rcast, GFP_ATOMIC);
-	if (!rcast)
+	if (!rcast) {
+		spin_unlock_bh(&ub->rcast_lock);
 		return -ENOMEM;
+	}
 
 	if (dst_cache_init(&rcast->dst_cache, GFP_ATOMIC)) {
+		spin_unlock_bh(&ub->rcast_lock);
 		kfree(rcast);
 		return -ENOMEM;
 	}
 
 	memcpy(&rcast->addr, addr, sizeof(struct udp_media_addr));
+	list_add_rcu(&rcast->list, &ub->rcast.list);
+	b->bcast_addr.broadcast = TIPC_REPLICAST_SUPPORT;
+	spin_unlock_bh(&ub->rcast_lock);
 
 	if (ntohs(addr->proto) == ETH_P_IP)
 		pr_info("New replicast peer: %pI4\n", &rcast->addr.ipv4);
@@ -325,8 +327,6 @@ static int tipc_udp_rcast_add(struct tipc_bearer *b,
 	else if (ntohs(addr->proto) == ETH_P_IPV6)
 		pr_info("New replicast peer: %pI6\n", &rcast->addr.ipv6);
 #endif
-	b->bcast_addr.broadcast = TIPC_REPLICAST_SUPPORT;
-	list_add_rcu(&rcast->list, &ub->rcast.list);
 	return 0;
 }
 
@@ -361,9 +361,6 @@ static int tipc_udp_rcast_disc(struct tipc_bearer *b, struct sk_buff *skb)
 		return 0;
 	}
 
-	if (likely(tipc_udp_is_known_peer(b, &src)))
-		return 0;
-
 	return tipc_udp_rcast_add(b, &src);
 }
 
@@ -644,9 +641,6 @@ int tipc_udp_nl_bearer_add(struct tipc_bearer *b, struct nlattr *attr)
 		return -EINVAL;
 	}
 
-	if (tipc_udp_is_known_peer(b, &addr))
-		return 0;
-
 	return tipc_udp_rcast_add(b, &addr);
 }
 
@@ -679,6 +673,7 @@ static int tipc_udp_enable(struct net *net, struct tipc_bearer *b,
 		return -ENOMEM;
 
 	INIT_LIST_HEAD(&ub->rcast.list);
+	spin_lock_init(&ub->rcast_lock);
 
 	if (!attrs[TIPC_NLA_BEARER_UDP_OPTS])
 		goto err;
@@ -819,10 +814,13 @@ static void cleanup_bearer(struct work_struct *work)
 	struct udp_replicast *rcast, *tmp;
 	struct tipc_net *tn;
 
+	spin_lock_bh(&ub->rcast_lock);
 	list_for_each_entry_safe(rcast, tmp, &ub->rcast.list, list) {
 		list_del_rcu(&rcast->list);
 		call_rcu_hurry(&rcast->rcu, rcast_free_rcu);
 	}
+	ub->disabled = true;
+	spin_unlock_bh(&ub->rcast_lock);
 
 	tn = tipc_net(sock_net(ub->sk));
 
Signature · 1 line
2.53.0
author maintainer
#262 lore raw

PATCH 262/744 net: txgbe: fix heap overflow when reading module EEPROM

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Chenguang Zhao <[email protected]>

[ Upstream commit 6a905a71fd43ce8b45f05044b11491337f232c9d ]

txgbe_read_eeprom_hostif() always copies round_up(length, 4) bytes into the caller buffer, which ethtool allocates with exactly 'length' bytes. A non-4-aligned length therefore causes an out-of-bounds write. Copy only the remaining bytes on the final dword instead.

---
 drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)
drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c +4 -2
index 8fc32df8e49a44..479c83f5d6454c 100644
@@ -96,11 +96,13 @@ int txgbe_read_eeprom_hostif(struct wx *wx,
 	dword_len = round_up(length, 4) >> 2;
 
 	for (i = 0; i < dword_len; i++) {
+		u32 copy_len = min_t(u32, 4, length - i * 4);
+
 		value = rd32a(wx, WX_FW2SW_MBOX, i + offset);
 		le32_to_cpus(&value);
 
-		memcpy(data, &value, 4);
-		data += 4;
+		memcpy(data, &value, copy_len);
+		data += copy_len;
 	}
 
 	return 0;
Signature · 1 line
2.53.0
author maintainer
#263 lore raw

PATCH 263/744 rds: Fix inet6_addr_lst NULL dereference when IPv6 is disabled

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Ilia Gavrilov <[email protected]>

[ Upstream commit 9c805e592a29be9e4e61ff1bd567da04aa8fd6f9 ]

When booting with the 'ipv6.disable=1' parameter, inet6_addr_lst is never initialized because inet6_init() exits before addrconf_init() is called to initialize it. An attempt to bind an RDS socket to an ipv6 address results in a crash in __ipv6_chk_addr_and_flags()

KASAN: null-ptr-deref in range [0x0000000000000008-0x000000000000000f]
RIP: 0010:__ipv6_chk_addr_and_flags+0x1df/0x7e0
Call Trace:
 <TASK>
 ipv6_chk_addr+0x3b/0x50
 rds_tcp_laddr_check+0x155/0x3b0 [rds_tcp]
 rds_trans_get_preferred+0x15d/0x2d0 [rds]
 ? trace_hardirqs_on+0x2d/0x110
 rds_bind+0x1433/0x1d60 [rds]
 ? rds_remove_bound+0xd50/0xd50 [rds]
 ? aa_af_perm+0x250/0x250
 ? __might_fault+0xde/0x190
 ? __sys_bind+0x1dc/0x210
 __sys_bind+0x1dc/0x210
 ? __ia32_sys_socketpair+0x100/0x100
 ? restore_fpregs_from_fpstate+0x53/0x100
 __x64_sys_bind+0x73/0xb0
 ? syscall_enter_from_user_mode+0x1c/0x50
 do_syscall_64+0x34/0x80
 entry_SYSCALL_64_after_hwframe+0x6e/0xd8
RIP: 0033:0x7f47f8269ea9
 </TASK>

The following code reproduces the issue:

struct sockaddr_in6 addr;
s = socket(PF_RDS, SOCK_SEQPACKET, 0);

memset(&addr, 0, sizeof(addr));
inet_pton(AF_INET6, ADDRESS, &addr.sin6_addr);
addr.sin6_family = AF_INET6;
addr.sin6_port = htons(PORT);

bind(s, &addr, sizeof(addr));

Found by InfoTeCS on behalf of Linux Verification Center (linuxtesting.org) with Syzkaller.

Fixes eee2fa6ab322 ("rds: Changing IP address internal representation to struct in6_addr")
Fixes 1e2b44e78eea ("rds: Enable RDS IPv6 support")
---
 net/rds/ib.c    | 4 ++++
 net/rds/ib_cm.c | 4 ++++
 net/rds/tcp.c   | 8 +++++---
 3 files changed, 13 insertions(+), 3 deletions(-)
net/rds/ib.c +4 -0
index 39f87272e071bf..8f9cf491984f12 100644
@@ -429,6 +429,10 @@ static int rds_ib_laddr_check_cm(struct net *net, const struct in6_addr *addr,
 		sa = (struct sockaddr *)&sin;
 	} else {
 #if IS_ENABLED(CONFIG_IPV6)
+		if (!ipv6_mod_enabled()) {
+			ret = -EADDRNOTAVAIL;
+			goto out;
+		}
 		memset(&sin6, 0, sizeof(sin6));
 		sin6.sin6_family = AF_INET6;
 		sin6.sin6_addr = *addr;
net/rds/ib_cm.c +4 -0
index 4001de0c495938..9b25486fde81c8 100644
@@ -810,6 +810,10 @@ int rds_ib_cm_handle_connect(struct rdma_cm_id *cm_id,
 	dp = event->param.conn.private_data;
 	if (isv6) {
 #if IS_ENABLED(CONFIG_IPV6)
+		if (!ipv6_mod_enabled()) {
+			err = -EOPNOTSUPP;
+			goto out;
+		}
 		dp_cmn = &dp->ricp_v6.dp_cmn;
 		saddr6 = &dp->ricp_v6.dp_saddr;
 		daddr6 = &dp->ricp_v6.dp_daddr;
net/rds/tcp.c +5 -3
index 5830b31a1f37b3..d3acad60315ab0 100644
@@ -347,9 +347,11 @@ int rds_tcp_laddr_check(struct net *net, const struct in6_addr *addr,
 		rcu_read_unlock();
 	}
 #if IS_ENABLED(CONFIG_IPV6)
-	ret = ipv6_chk_addr(net, addr, dev, 0);
-	if (ret)
-		return 0;
+	if (ipv6_mod_enabled()) {
+		ret = ipv6_chk_addr(net, addr, dev, 0);
+		if (ret)
+			return 0;
+	}
 #endif
 	return -EADDRNOTAVAIL;
 }
Signature · 1 line
2.53.0
author maintainer
#264 lore raw

PATCH 264/744 net: txgbe: fix FDIR filter leak on remove

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Chenguang Zhao <[email protected]>

[ Upstream commit ecaa37826340520664a4e5522f803ff48fc3f564 ]

Perfect FDIR filters can be added while the interface is down and are kept on the software list for later restore. unregister_netdev() only calls ndo_stop when the device is up, so txgbe_fdir_filter_exit() in txgbe_close() is skipped in that case and the filters are leaked on driver remove. Free the filter list from txgbe_remove() as well.

Fixes 4bdb441105dc ("net: txgbe: support Flow Director perfect filters")
---
 drivers/net/ethernet/wangxun/txgbe/txgbe_main.c | 1 +
 1 file changed, 1 insertion(+)
drivers/net/ethernet/wangxun/txgbe/txgbe_main.c +1 -0
index 8b7c3753bb6acc..1f633bdf1972f6 100644
@@ -951,6 +951,7 @@ static void txgbe_remove(struct pci_dev *pdev)
 	netdev = wx->netdev;
 	wx_disable_sriov(wx);
 	unregister_netdev(netdev);
+	txgbe_fdir_filter_exit(wx);
 
 	timer_shutdown_sync(&wx->service_timer);
 	cancel_work_sync(&wx->service_task);
Signature · 1 line
2.53.0
author maintainer
#265 lore raw

PATCH 265/744 sctp: fix auth_chunk_list capacity check in sctp_auth_ep_add_chunkid

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: HanQuan <[email protected]>

[ Upstream commit ff04b26794a16a8a879eb4fd2c02c2d6b03850e9 ]

sctp_auth_ep_add_chunkid() uses SCTP_NUM_CHUNK_TYPES (20) as the capacity limit for ep->auth_chunk_list, allowing it to hold up to 20 chunk entries (param_hdr.length up to 24). However, the copy destination asoc->c.auth_chunks in struct sctp_cookie is only SCTP_AUTH_MAX_CHUNKS (16) entries (20 bytes). When more than 16 chunks are added, sctp_association_init() memcpy overflows the destination by up to 4 bytes.

Fix by using SCTP_AUTH_MAX_CHUNKS as the capacity limit, matching the destination capacity.

---
 net/sctp/auth.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
net/sctp/auth.c +1 -1
index be9782760f5093..c901d373af803c 100644
@@ -672,7 +672,7 @@ int sctp_auth_ep_add_chunkid(struct sctp_endpoint *ep, __u8 chunk_id)
 	/* Check if we can add this chunk to the array */
 	param_len = ntohs(p->param_hdr.length);
 	nchunks = param_len - sizeof(struct sctp_paramhdr);
-	if (nchunks == SCTP_NUM_CHUNK_TYPES)
+	if (nchunks == SCTP_AUTH_MAX_CHUNKS)
 		return -EINVAL;
 
 	p->chunks[nchunks] = chunk_id;
Signature · 1 line
2.53.0
author maintainer
#266 lore raw

PATCH 266/744 pds_core: fix deadlock between reset thread and remove

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Nikhil P. Rao <[email protected]>

[ Upstream commit ab0eec0ff0a421737a37f510ceab5c6ea59cd05a ]

pci_reset_function() acquires device_lock before performing the reset. pdsc_remove() is called by the PCI core with device_lock already held. If pdsc_pci_reset_thread() is running when pdsc_remove() is called, destroy_workqueue() will block waiting for the work to complete, while the work is blocked waiting for device_lock - deadlock.

Use pci_try_reset_function() which uses pci_dev_trylock() internally. This acquires both the device lock and the PCI config access lock without blocking - if either lock is contended, it returns -EAGAIN immediately. This avoids the deadlock while also ensuring proper config space access serialization during the reset.

The pci_dev_get/put calls are also removed as they were unnecessary - the driver-owned workqueue is destroyed in pdsc_remove(), guaranteeing the work completes before remove returns. The PCI core holds its reference to pci_dev throughout the entire unbind sequence.

---
 drivers/net/ethernet/amd/pds_core/core.c | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)
drivers/net/ethernet/amd/pds_core/core.c +4 -3
index 705cab7b07273d..b6d4b30147232a 100644
@@ -602,9 +602,10 @@ void pdsc_pci_reset_thread(struct work_struct *work)
 	struct pdsc *pdsc = container_of(work, struct pdsc, pci_reset_work);
 	struct pci_dev *pdev = pdsc->pdev;
 
-	pci_dev_get(pdev);
-	pci_reset_function(pdev);
-	pci_dev_put(pdev);
+	/* Use try variant to avoid deadlock with pdsc_remove().
+	 * If lock is contended, the watchdog timer will retry.
+	 */
+	pci_try_reset_function(pdev);
 }
 
 static void pdsc_check_pci_health(struct pdsc *pdsc)
Signature · 1 line
2.53.0
author maintainer
#267 lore raw

PATCH 267/744 pds_core: fix use-after-free on workqueue during remove

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Nikhil P. Rao <[email protected]>

[ Upstream commit 0ad134881508c36b65c1a8864f8bec53adbd3327 ]

In pdsc_remove(), the workqueue is destroyed before pdsc_teardown() is called. This ordering allows two paths to queue work on the destroyed workqueue:

1. If pdsc_teardown() -> pdsc_devcmd_reset() times out, the error path in pdsc_devcmd_locked() queues health_work.

2. A NotifyQ event can trigger the ISR and queue work before free_irq() is called in pdsc_teardown().

Fix by moving destroy_workqueue() after pdsc_teardown() so the workqueue outlives every queuer; destroy_workqueue() then flushes any work still pending.

Draining the queued work also requires ordering the teardown so the resources that work touches are freed last:

- In pdsc_qcq_free(), after freeing the interrupt, cancel_work_sync()

    the queue's work and only then clear qcq->intx, so
    pdsc_process_adminq()'s read of qcq->intx for interrupt-credit
    return cannot race with the clear.

- Free adminqcq before notifyqcq: the shared adminq ISR is released when adminqcq is freed, and the adminq work accesses notifyqcq, so both must be stopped before notifyqcq is freed.

---
 drivers/net/ethernet/amd/pds_core/core.c | 14 ++++++++++----
 drivers/net/ethernet/amd/pds_core/main.c |  5 +++--
 2 files changed, 13 insertions(+), 6 deletions(-)
drivers/net/ethernet/amd/pds_core/core.c +10 -4
index b6d4b30147232a..a73a2ab9060e62 100644
@@ -110,7 +110,6 @@ static void pdsc_qcq_intr_free(struct pdsc *pdsc, struct pdsc_qcq *qcq)
 		return;
 
 	pdsc_intr_free(pdsc, qcq->intx);
-	qcq->intx = PDS_CORE_INTR_INDEX_NOT_ASSIGNED;
 }
 
 static int pdsc_qcq_intr_alloc(struct pdsc *pdsc, struct pdsc_qcq *qcq)
@@ -145,6 +144,12 @@ void pdsc_qcq_free(struct pdsc *pdsc, struct pdsc_qcq *qcq)
 
 	pdsc_qcq_intr_free(pdsc, qcq);
 
+	/* Drain any work queued by ISR before it was freed above */
+	if (qcq->work.func)
+		cancel_work_sync(&qcq->work);
+
+	qcq->intx = PDS_CORE_INTR_INDEX_NOT_ASSIGNED;
+
 	if (qcq->q_base)
 		dma_free_coherent(dev, qcq->q_size,
 				  qcq->q_base, qcq->q_base_pa);
@@ -304,8 +309,11 @@ int pdsc_qcq_alloc(struct pdsc *pdsc, unsigned int type, unsigned int index,
 
 static void pdsc_core_uninit(struct pdsc *pdsc)
 {
-	pdsc_qcq_free(pdsc, &pdsc->notifyqcq);
+	/* Free adminqcq first: its work accesses notifyqcq, so we must
+	 * disable its IRQ and drain its work before freeing notifyqcq.
+	 */
 	pdsc_qcq_free(pdsc, &pdsc->adminqcq);
+	pdsc_qcq_free(pdsc, &pdsc->notifyqcq);
 
 	if (pdsc->kern_dbpage) {
 		iounmap(pdsc->kern_dbpage);
@@ -477,8 +485,6 @@ void pdsc_teardown(struct pdsc *pdsc, bool removing)
 {
 	if (!pdsc->pdev->is_virtfn)
 		pdsc_devcmd_reset(pdsc);
-	if (pdsc->adminqcq.work.func)
-		cancel_work_sync(&pdsc->adminqcq.work);
 
 	pdsc_core_uninit(pdsc);
 
drivers/net/ethernet/amd/pds_core/main.c +3 -2
index 22db78343eb075..638b9c7a509d9d 100644
@@ -435,8 +435,6 @@ static void pdsc_remove(struct pci_dev *pdev)
 		pdsc_auxbus_dev_del(pdsc, pdsc, &pdsc->padev);
 
 		timer_shutdown_sync(&pdsc->wdtimer);
-		if (pdsc->wq)
-			destroy_workqueue(pdsc->wq);
 
 		mutex_lock(&pdsc->config_lock);
 		set_bit(PDSC_S_STOPPING_DRIVER, &pdsc->state);
@@ -444,6 +442,9 @@ static void pdsc_remove(struct pci_dev *pdev)
 		pdsc_stop(pdsc);
 		pdsc_teardown(pdsc, PDSC_TEARDOWN_REMOVING);
 		mutex_unlock(&pdsc->config_lock);
+
+		if (pdsc->wq)
+			destroy_workqueue(pdsc->wq);
 		mutex_destroy(&pdsc->config_lock);
 		mutex_destroy(&pdsc->devcmd_lock);
 
Signature · 1 line
2.53.0
author maintainer
#268 lore raw

PATCH 268/744 pds_core: yield the CPU while waiting for the adminq to drain

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Nikhil P. Rao <[email protected]>

[ Upstream commit a11f0b8a204296fe7db9eaec53441012222cb004 ]

pdsc_adminq_wait_and_dec_once_unused() busy-waits for adminq_refcnt to drop to one:

	while (!refcount_dec_if_one(&pdsc->adminq_refcnt))
		cpu_relax();

The refcount is held by pdsc_adminq_post() for the duration of an in-flight command, which can wait up to devcmd_timeout seconds (PDS_CORE_DEVCMD_TIMEOUT is 5) for the hardware to complete. cpu_relax() is not a reschedule point, so on a non-preemptible kernel this loop can spin on the CPU for several seconds, starving other tasks on that core.

Add cond_resched() to the loop so the waiter yields to other runnable tasks while it polls, keeping cpu_relax() as the busy-wait hint between checks.

---
 drivers/net/ethernet/amd/pds_core/core.c | 1 +
 1 file changed, 1 insertion(+)
drivers/net/ethernet/amd/pds_core/core.c +1 -0
index a73a2ab9060e62..34181ac8479919 100644
@@ -535,6 +535,7 @@ static void pdsc_adminq_wait_and_dec_once_unused(struct pdsc *pdsc)
 		dev_dbg_ratelimited(pdsc->dev, "%s: adminq in use\n",
 				    __func__);
 		cpu_relax();
+		cond_resched();
 	}
 }
 
Signature · 1 line
2.53.0
author maintainer
#269 lore raw

PATCH 269/744 pds_core: order completion reads after the ownership check

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Nikhil P. Rao <[email protected]>

[ Upstream commit dd6b1cc748cd28147c113f9daa76393916ad9494 ]

pdsc_process_adminq() and pdsc_process_notifyq() decide a completion is valid from its ownership field - the color bit for the adminq, the event id for the notifyq - then read the rest of the descriptor, with no barrier in between.

On a weakly ordered architecture the CPU may read the payload first. Add dma_rmb() between the ownership read and the payload reads.

---
 drivers/net/ethernet/amd/pds_core/adminq.c | 12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)
drivers/net/ethernet/amd/pds_core/adminq.c +11 -1
index 097bb092bdb8cd..eadb4b604fbe3c 100644
@@ -18,7 +18,13 @@ static int pdsc_process_notifyq(struct pdsc_qcq *qcq)
 	comp = cq_info->comp;
 	eid = le64_to_cpu(comp->event.eid);
 	while (eid > pdsc->last_eid) {
-		u16 ecode = le16_to_cpu(comp->event.ecode);
+		u16 ecode;
+
+		/* Order the payload read after the event id, the field the
+		 * driver uses to detect a new completion.
+		 */
+		dma_rmb();
+		ecode = le16_to_cpu(comp->event.ecode);
 
 		switch (ecode) {
 		case PDS_EVENT_LINK_CHANGE:
@@ -101,6 +107,10 @@ void pdsc_process_adminq(struct pdsc_qcq *qcq)
 	spin_lock_irqsave(&pdsc->adminq_lock, irqflags);
 	comp = cq->info[cq->tail_idx].comp;
 	while (pdsc_color_match(comp->color, cq->done_color)) {
+		/* Order the payload reads after the color bit, the field the
+		 * driver uses to detect a new completion.
+		 */
+		dma_rmb();
 		q_info = &q->info[q->tail_idx];
 		q->tail_idx = (q->tail_idx + 1) & (q->num_descs - 1);
 
Signature · 1 line
2.53.0
author maintainer
#270 lore raw

PATCH 270/744 pds_core: fix auxiliary device add/del races

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Nikhil P. Rao <[email protected]>

[ Upstream commit bfa33cd513c7ceb93c5a4c30e5662acd73c0a916 ]

Two paths add or delete the same slot (pf->vfs[vf_id].padev): a VF's pdsc_reset_done() and the PF's devlink enable_vnet/disable_vnet handler. They serialize on config_lock, but neither guards the slot under it correctly.

add() registers and stores a new auxiliary device without first checking the slot, so a second add of an already-populated slot leaks the first device. del() makes that check outside config_lock, so two concurrent dels can both pass it; the first clears the slot, and the second dereferences a NULL pointer.

Check and update the slot under config_lock in both paths.

---
 drivers/net/ethernet/amd/pds_core/auxbus.c | 17 ++++++++++++++---
 1 file changed, 14 insertions(+), 3 deletions(-)
drivers/net/ethernet/amd/pds_core/auxbus.c +14 -3
index 73b3481220b1ac..3acafe10a6d5ba 100644
@@ -177,17 +177,21 @@ void pdsc_auxbus_dev_del(struct pdsc *cf, struct pdsc *pf,
 {
 	struct pds_auxiliary_dev *padev;
 
-	if (!*pd_ptr)
-		return;
-
 	mutex_lock(&pf->config_lock);
 
+	/* A concurrent del may have already torn this device down and
+	 * cleared it.
+	 */
 	padev = *pd_ptr;
+	if (!padev)
+		goto out_unlock;
+
 	pds_client_unregister(pf, padev->client_id);
 	auxiliary_device_delete(&padev->aux_dev);
 	auxiliary_device_uninit(&padev->aux_dev);
 	*pd_ptr = NULL;
 
+out_unlock:
 	mutex_unlock(&pf->config_lock);
 }
 
@@ -210,6 +214,13 @@ int pdsc_auxbus_dev_add(struct pdsc *cf, struct pdsc *pf,
 
 	mutex_lock(&pf->config_lock);
 
+	/* Nothing to do if the aux device is already present.  This also
+	 * guards against a second add overwriting *pd_ptr and leaking the
+	 * first, symmetric with the check in pdsc_auxbus_dev_del().
+	 */
+	if (*pd_ptr)
+		goto out_unlock;
+
 	mask = BIT_ULL(PDSC_S_FW_DEAD) |
 	       BIT_ULL(PDSC_S_STOPPING_DRIVER);
 	if (cf->state & mask) {
Signature · 1 line
2.53.0
author maintainer
#271 lore raw

PATCH 271/744 pds_core: check for workqueue allocation failure

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Nikhil P. Rao <[email protected]>

[ Upstream commit 3a660ca49e2c3807bffe0519db3cff677a5906e0 ]

pdsc_init_pf() does not check whether create_singlethread_workqueue() succeeded.

Fail probe on failure. The workqueue is set up before the timer and mutexes, so its failure path must unwind only the earlier setup.

---
 drivers/net/ethernet/amd/pds_core/main.c | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)
drivers/net/ethernet/amd/pds_core/main.c +7 -2
index 638b9c7a509d9d..8d94a4d70395ed 100644
@@ -238,6 +238,10 @@ static int pdsc_init_pf(struct pdsc *pdsc)
 	/* General workqueue and timer, but don't start timer yet */
 	snprintf(wq_name, sizeof(wq_name), "%s.%d", PDS_CORE_DRV_NAME, pdsc->uid);
 	pdsc->wq = create_singlethread_workqueue(wq_name);
+	if (!pdsc->wq) {
+		err = -ENOMEM;
+		goto err_out_unmap_bars;
+	}
 	INIT_WORK(&pdsc->health_work, pdsc_health_thread);
 	INIT_WORK(&pdsc->pci_reset_work, pdsc_pci_reset_thread);
 	timer_setup(&pdsc->wdtimer, pdsc_wdtimer_cb, 0);
@@ -253,7 +257,7 @@ static int pdsc_init_pf(struct pdsc *pdsc)
 	err = pdsc_setup(pdsc, PDSC_SETUP_INIT);
 	if (err) {
 		mutex_unlock(&pdsc->config_lock);
-		goto err_out_unmap_bars;
+		goto err_out_shutdown_timer;
 	}
 
 	err = pdsc_start(pdsc);
@@ -305,13 +309,14 @@ static int pdsc_init_pf(struct pdsc *pdsc)
 	pdsc_stop(pdsc);
 err_out_teardown:
 	pdsc_teardown(pdsc, PDSC_TEARDOWN_REMOVING);
-err_out_unmap_bars:
+err_out_shutdown_timer:
 	timer_shutdown_sync(&pdsc->wdtimer);
 	if (pdsc->wq)
 		destroy_workqueue(pdsc->wq);
 	mutex_destroy(&pdsc->config_lock);
 	mutex_destroy(&pdsc->devcmd_lock);
 	pci_free_irq_vectors(pdsc->pdev);
+err_out_unmap_bars:
 	pdsc_unmap_bars(pdsc);
 err_out_release_regions:
 	pci_release_regions(pdsc->pdev);
Signature · 1 line
2.53.0
author maintainer
#272 lore raw

PATCH 272/744 accel: ethosu: Fix element size accounting for cmd stream validation

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Rob Herring (Arm) <[email protected]>

[ Upstream commit 18a551482a4a326790698b273e76d7575a51a57d ]

There are 2 issues with the element size handling in the command stream validation which result in too small of a size calculated when the element size is 16/32/64 bits.

For NHWC format, the element size is simply missing from the calculation.

The bitfield for the element size is different between IFM/IFM2 and OFM. IFM and IFM2 encode the precision in parameter bits 2:3, while OFM uses bits 1:2.

---
 drivers/accel/ethosu/ethosu_gem.c | 19 ++++++++++---------
 1 file changed, 10 insertions(+), 9 deletions(-)
drivers/accel/ethosu/ethosu_gem.c +10 -9
index 3401883e207fc8..1daff32610c71b 100644
@@ -194,7 +194,7 @@ static u64 dma_length(struct ethosu_validated_cmdstream_info *info,
 
 static u64 feat_matrix_length(struct ethosu_validated_cmdstream_info *info,
 			      struct feat_matrix *fm,
-			      u32 x, u32 y, u32 c)
+			      u32 x, u32 y, u32 c, bool ofm)
 {
 	u32 element_size, storage = fm->precision >> 14;
 	int tile = 0;
@@ -231,10 +231,11 @@ static u64 feat_matrix_length(struct ethosu_validated_cmdstream_info *info,
 
 	switch ((fm->precision >> 6) & 0x3) { // format
 	case 0: //nhwc:
-		addr += x * fm->stride_x + c;
+		element_size = BIT((fm->precision >> (ofm ? 1 : 2)) & 0x3);
+		addr += x * fm->stride_x + c * element_size;
 		break;
 	case 1: //nhcwb16:
-		element_size = BIT((fm->precision >> 1) & 0x3);
+		element_size = BIT((fm->precision >> (ofm ? 1 : 2)) & 0x3);
 
 		addr += (c / 16) * fm->stride_c + (16 * x + (c & 0xf)) * element_size;
 		break;
@@ -268,7 +269,7 @@ static int calc_sizes(struct drm_device *ddev,
 			return -EINVAL;
 
 		len = feat_matrix_length(info, &st->ifm, ifm_width,
-					 ifm_height, st->ifm.depth);
+					 ifm_height, st->ifm.depth, false);
 		dev_dbg(ddev->dev, "op %d: IFM:%d:0x%llx-0x%llx\n",
 			op, st->ifm.region, st->ifm.base[0], len);
 		if (len == U64_MAX)
@@ -277,7 +278,7 @@ static int calc_sizes(struct drm_device *ddev,
 
 	if (ifm2) {
 		len = feat_matrix_length(info, &st->ifm2, st->ifm.depth,
-					 0, st->ofm.depth);
+					 0, st->ofm.depth, false);
 		dev_dbg(ddev->dev, "op %d: IFM2:%d:0x%llx-0x%llx\n",
 			op, st->ifm2.region, st->ifm2.base[0], len);
 		if (len == U64_MAX)
@@ -309,7 +310,7 @@ static int calc_sizes(struct drm_device *ddev,
 	}
 
 	len = feat_matrix_length(info, &st->ofm, st->ofm.width,
-				 st->ofm.height[2], st->ofm.depth);
+				 st->ofm.height[2], st->ofm.depth, true);
 	dev_dbg(ddev->dev, "op %d: OFM:%d:0x%llx-0x%llx\n",
 		op, st->ofm.region, st->ofm.base[0], len);
 	if (len == U64_MAX)
@@ -333,7 +334,7 @@ static int calc_sizes_elemwise(struct drm_device *ddev,
 		depth = st->ifm.broadcast & 0x4 ? 0 : st->ofm.depth;
 
 		len = feat_matrix_length(info, &st->ifm, width,
-					 height, depth);
+					 height, depth, false);
 		dev_dbg(ddev->dev, "op %d: IFM:%d:0x%llx-0x%llx\n",
 			op, st->ifm.region, st->ifm.base[0], len);
 		if (len == U64_MAX)
@@ -346,7 +347,7 @@ static int calc_sizes_elemwise(struct drm_device *ddev,
 		depth = st->ifm2.broadcast & 0x4 ? 0 : st->ofm.depth;
 
 		len = feat_matrix_length(info, &st->ifm2, width,
-					 height, depth);
+					 height, depth, false);
 		dev_dbg(ddev->dev, "op %d: IFM2:%d:0x%llx-0x%llx\n",
 			op, st->ifm2.region, st->ifm2.base[0], len);
 		if (len == U64_MAX)
@@ -354,7 +355,7 @@ static int calc_sizes_elemwise(struct drm_device *ddev,
 	}
 
 	len = feat_matrix_length(info, &st->ofm, st->ofm.width,
-				 st->ofm.height[2], st->ofm.depth);
+				 st->ofm.height[2], st->ofm.depth, true);
 	dev_dbg(ddev->dev, "op %d: OFM:%d:0x%llx-0x%llx\n",
 		op, st->ofm.region, st->ofm.base[0], len);
 	if (len == U64_MAX)
Signature · 1 line
2.53.0
author maintainer
#273 lore raw

PATCH 273/744 accel: ethosu: Handle U85 internal chaining buffer

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Rob Herring (Arm) <[email protected]>

[ Upstream commit 6b7e0066294d23ad1fd37f4326c32e8090fb8b65 ]

The Ethos-U85 supports an internal chaining buffer as temporary storage between some operations. When chaining is activated, the IFM/OFM region setting selects a chaining buffer rather than a region, and the IFM/OFM base addresses don't matter. In this case, the feature matrix size calculations should be skipped. Otherwise, the command stream will be intermittently rejected depending on prior feature matrix base addresses.

---
 drivers/accel/ethosu/ethosu_gem.c | 35 +++++++++++++++++++++++--------
 1 file changed, 26 insertions(+), 9 deletions(-)
drivers/accel/ethosu/ethosu_gem.c +26 -9
index 1daff32610c71b..d50fed64d4d937 100644
@@ -192,7 +192,15 @@ static u64 dma_length(struct ethosu_validated_cmdstream_info *info,
 	return len;
 }
 
-static u64 feat_matrix_length(struct ethosu_validated_cmdstream_info *info,
+static bool feat_matrix_chained(struct ethosu_device *edev, struct feat_matrix *fm)
+{
+	u32 storage = fm->precision >> 14;
+
+	return !ethosu_is_u65(edev) && storage == 2;
+}
+
+static u64 feat_matrix_length(struct ethosu_device *edev,
+			      struct ethosu_validated_cmdstream_info *info,
 			      struct feat_matrix *fm,
 			      u32 x, u32 y, u32 c, bool ofm)
 {
@@ -203,6 +211,9 @@ static u64 feat_matrix_length(struct ethosu_validated_cmdstream_info *info,
 	if (fm->region < 0)
 		return U64_MAX;
 
+	if (feat_matrix_chained(edev, fm))
+		return 0;
+
 	switch (storage) {
 	case 0:
 		if (x >= fm->width0 + 1) {
@@ -223,6 +234,8 @@ static u64 feat_matrix_length(struct ethosu_validated_cmdstream_info *info,
 			tile = 1;
 		}
 		break;
+	default:
+		return U64_MAX;
 	}
 	if (fm->base[tile] == U64_MAX)
 		return U64_MAX;
@@ -251,6 +264,7 @@ static int calc_sizes(struct drm_device *ddev,
 		      u16 op, struct cmd_state *st,
 		      bool ifm, bool ifm2, bool weight, bool scale)
 {
+	struct ethosu_device *edev = to_ethosu_device(ddev);
 	u64 len;
 
 	if (ifm) {
@@ -268,7 +282,7 @@ static int calc_sizes(struct drm_device *ddev,
 		if (ifm_height < 0 || ifm_width < 0)
 			return -EINVAL;
 
-		len = feat_matrix_length(info, &st->ifm, ifm_width,
+		len = feat_matrix_length(edev, info, &st->ifm, ifm_width,
 					 ifm_height, st->ifm.depth, false);
 		dev_dbg(ddev->dev, "op %d: IFM:%d:0x%llx-0x%llx\n",
 			op, st->ifm.region, st->ifm.base[0], len);
@@ -277,7 +291,7 @@ static int calc_sizes(struct drm_device *ddev,
 	}
 
 	if (ifm2) {
-		len = feat_matrix_length(info, &st->ifm2, st->ifm.depth,
+		len = feat_matrix_length(edev, info, &st->ifm2, st->ifm.depth,
 					 0, st->ofm.depth, false);
 		dev_dbg(ddev->dev, "op %d: IFM2:%d:0x%llx-0x%llx\n",
 			op, st->ifm2.region, st->ifm2.base[0], len);
@@ -309,13 +323,14 @@ static int calc_sizes(struct drm_device *ddev,
 			    st->scale[0].base + st->scale[0].length);
 	}
 
-	len = feat_matrix_length(info, &st->ofm, st->ofm.width,
+	len = feat_matrix_length(edev, info, &st->ofm, st->ofm.width,
 				 st->ofm.height[2], st->ofm.depth, true);
 	dev_dbg(ddev->dev, "op %d: OFM:%d:0x%llx-0x%llx\n",
 		op, st->ofm.region, st->ofm.base[0], len);
 	if (len == U64_MAX)
 		return -EINVAL;
-	info->output_region[st->ofm.region] = true;
+	if (!feat_matrix_chained(edev, &st->ofm))
+		info->output_region[st->ofm.region] = true;
 
 	return 0;
 }
@@ -325,6 +340,7 @@ static int calc_sizes_elemwise(struct drm_device *ddev,
 			       u16 op, struct cmd_state *st,
 			       bool ifm, bool ifm2)
 {
+	struct ethosu_device *edev = to_ethosu_device(ddev);
 	u32 height, width, depth;
 	u64 len;
 
@@ -333,7 +349,7 @@ static int calc_sizes_elemwise(struct drm_device *ddev,
 		width = st->ifm.broadcast & 0x2 ? 0 : st->ofm.width;
 		depth = st->ifm.broadcast & 0x4 ? 0 : st->ofm.depth;
 
-		len = feat_matrix_length(info, &st->ifm, width,
+		len = feat_matrix_length(edev, info, &st->ifm, width,
 					 height, depth, false);
 		dev_dbg(ddev->dev, "op %d: IFM:%d:0x%llx-0x%llx\n",
 			op, st->ifm.region, st->ifm.base[0], len);
@@ -346,7 +362,7 @@ static int calc_sizes_elemwise(struct drm_device *ddev,
 		width = st->ifm2.broadcast & 0x2 ? 0 : st->ofm.width;
 		depth = st->ifm2.broadcast & 0x4 ? 0 : st->ofm.depth;
 
-		len = feat_matrix_length(info, &st->ifm2, width,
+		len = feat_matrix_length(edev, info, &st->ifm2, width,
 					 height, depth, false);
 		dev_dbg(ddev->dev, "op %d: IFM2:%d:0x%llx-0x%llx\n",
 			op, st->ifm2.region, st->ifm2.base[0], len);
@@ -354,13 +370,14 @@ static int calc_sizes_elemwise(struct drm_device *ddev,
 			return -EINVAL;
 	}
 
-	len = feat_matrix_length(info, &st->ofm, st->ofm.width,
+	len = feat_matrix_length(edev, info, &st->ofm, st->ofm.width,
 				 st->ofm.height[2], st->ofm.depth, true);
 	dev_dbg(ddev->dev, "op %d: OFM:%d:0x%llx-0x%llx\n",
 		op, st->ofm.region, st->ofm.base[0], len);
 	if (len == U64_MAX)
 		return -EINVAL;
-	info->output_region[st->ofm.region] = true;
+	if (!feat_matrix_chained(edev, &st->ofm))
+		info->output_region[st->ofm.region] = true;
 
 	return 0;
 }
Signature · 1 line
2.53.0
author maintainer
#274 lore raw

PATCH 274/744 sctp: validate stream count in sctp_process_strreset_inreq()

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Cen Zhang (Microsoft) <[email protected]>

[ Upstream commit 18ae07691d43183d270de8be9dc8e027906015d9 ]

When processing a RESET_IN_REQUEST from a peer, sctp_process_strreset_inreq() derives the stream count from the parameter length but does not check whether the resulting RESET_OUT_REQUEST would exceed SCTP_MAX_CHUNK_LEN.

The OUT request header (sctp_strreset_outreq, 16 bytes) is 8 bytes larger than the IN request header (sctp_strreset_inreq, 8 bytes). Generally, the IP payload is bounded to 65535 bytes, so the stream list cannot be large enough to trigger the overflow. However, on interfaces with MTU > 65535 (e.g., loopback with IPv6 jumbograms), a stream list that fits within the incoming IN parameter can cause a __u16 overflow in sctp_make_strreset_req() when computing the OUT request size, leading to an undersized skb allocation and a kernel BUG:

net/core/skbuff.c:207 skb_panic
net/core/skbuff.c:2625 skb_put
net/sctp/sm_make_chunk.c:1535 sctp_addto_chunk net/sctp/sm_make_chunk.c:3695 sctp_make_strreset_req
net/sctp/stream.c:655 sctp_process_strreset_inreq

The local setsockopt path validates the generated reset request size. However, for an incoming-only reset, it accounts for the smaller IN request even though the peer must generate an OUT request with the same stream list. Such a request cannot be completed successfully by the peer.

Reject peer IN requests whose corresponding OUT request would exceed SCTP_MAX_CHUNK_LEN. Also tighten the local check so it does not send an IN request that would require an oversized OUT request from the peer.

---
 net/sctp/stream.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)
net/sctp/stream.c +5 -1
index 5c2fdedea088ff..34ffe6c945a4bb 100644
@@ -308,7 +308,8 @@ int sctp_send_reset_streams(struct sctp_association *asoc,
 					goto out;
 
 			param_len += str_nums * sizeof(__u16) +
-				     sizeof(struct sctp_strreset_inreq);
+				     (out ? sizeof(struct sctp_strreset_inreq)
+					  : sizeof(struct sctp_strreset_outreq));
 		}
 
 		if (param_len > SCTP_MAX_CHUNK_LEN -
@@ -639,6 +640,9 @@ struct sctp_chunk *sctp_process_strreset_inreq(
 
 	nums = (ntohs(param.p->length) - sizeof(*inreq)) / sizeof(__u16);
 	str_p = inreq->list_of_streams;
+	if (nums * sizeof(__u16) + sizeof(struct sctp_strreset_outreq) >
+	    SCTP_MAX_CHUNK_LEN - sizeof(struct sctp_reconf_chunk))
+		goto out;
 	for (i = 0; i < nums; i++) {
 		if (ntohs(str_p[i]) >= stream->outcnt) {
 			result = SCTP_STRRESET_ERR_WRONG_SSN;
Signature · 1 line
2.53.0
author maintainer
#275 lore raw

PATCH 275/744 net: mctp i3c: clean up notifier and buses if driver register fails

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Myeonghun Pak <[email protected]>

[ Upstream commit 03d1057305ef17ac3f5936ac1580bc9a1a826e14 ]

mctp_i3c_mod_init() registers the I3C bus notifier and then walks the existing buses with i3c_for_each_bus_locked(mctp_i3c_bus_add_new, NULL) before registering the I3C device driver. If i3c_driver_register() fails, the function returns the error directly, leaving the notifier registered and every mctp_i3c_bus object created for the existing buses allocated. The notifier is left pointing into the module that failed to load and the bus list is leaked.

Mirror the module exit path on this failure: unregister the notifier and tear down the buses that were added before returning the error.

This issue was identified during our ongoing static-analysis research while reviewing kernel code.

---
 drivers/net/mctp/mctp-i3c.c | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)
drivers/net/mctp/mctp-i3c.c +7 -4
index 6d2bbae7477b39..88d9e36cd4a2bc 100644
@@ -731,18 +731,21 @@ static __init int mctp_i3c_mod_init(void)
 	int rc;
 
 	rc = i3c_register_notifier(&mctp_i3c_notifier);
-	if (rc < 0) {
-		i3c_driver_unregister(&mctp_i3c_driver);
+	if (rc < 0)
 		return rc;
-	}
 
 	i3c_for_each_bus_locked(mctp_i3c_bus_add_new, NULL);
 
 	rc = i3c_driver_register(&mctp_i3c_driver);
 	if (rc < 0)
-		return rc;
+		goto err_unregister_notifier;
 
 	return 0;
+
+err_unregister_notifier:
+	i3c_unregister_notifier(&mctp_i3c_notifier);
+	mctp_i3c_bus_remove_all();
+	return rc;
 }
 
 static __exit void mctp_i3c_mod_exit(void)
Signature · 1 line
2.53.0
author maintainer
#276 lore raw

PATCH 276/744 tls: device: push pending open record on splice EOF

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Rishikesh Jethwani <[email protected]>

[ Upstream commit eaa39f9f8ac8c1d032cd26b9cd572804e9d7683f ]

On kTLS device-offload sockets, sendfile() with count > EOF can reach ->splice_eof() with a fully assembled but still-open TLS record left pending. tls_device_splice_eof() only flushes partially sent records, so an abrupt close() can drop the final record and the peer receives a short file.

Fix tls_device_splice_eof() to also push pending open records. This matches the software path, where splice EOF already flushes pending open records.

---
 net/tls/tls_device.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)
net/tls/tls_device.c +4 -2
index 99c8eff9783e68..33c42f65364a22 100644
@@ -595,13 +595,15 @@ void tls_device_splice_eof(struct socket *sock)
 	struct tls_context *tls_ctx = tls_get_ctx(sk);
 	struct iov_iter iter = {};
 
-	if (!tls_is_partially_sent_record(tls_ctx))
+	if (!tls_is_partially_sent_record(tls_ctx) &&
+	    !tls_is_pending_open_record(tls_ctx))
 		return;
 
 	mutex_lock(&tls_ctx->tx_lock);
 	lock_sock(sk);
 
-	if (tls_is_partially_sent_record(tls_ctx)) {
+	if (tls_is_partially_sent_record(tls_ctx) ||
+	    tls_is_pending_open_record(tls_ctx)) {
 		iov_iter_bvec(&iter, ITER_SOURCE, NULL, 0, 0);
 		tls_push_data(sk, &iter, 0, 0, TLS_RECORD_TYPE_DATA);
 	}
Signature · 1 line
2.53.0
author maintainer
#277 lore raw

PATCH 277/744 selftests: af_unix: add USER_NS config

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Matthieu Baerts (NGI0) <[email protected]>

[ Upstream commit f8b1abed736111f914b2c567d9a3db1f71e788e8 ]

This is required to use unshare(CLONE_NEWUSER).

This has not been seen on NIPA before, because the 'af_unix' tests are executed with the 'net' ones, merging their config files. USER_NS is present in tools/testing/selftests/net/config.

This issue is visible when only the af_unix config is used on top of the default one. This is the recommended way to execute selftest targets.

Fixes ac011361bd4f ("af_unix: Add test for sock_diag and UDIAG_SHOW_UID.")
---
 tools/testing/selftests/net/af_unix/config | 1 +
 1 file changed, 1 insertion(+)
tools/testing/selftests/net/af_unix/config +1 -0
index b5429c15a53c75..41dbb03c747eb7 100644
@@ -1,3 +1,4 @@
 CONFIG_AF_UNIX_OOB=y
 CONFIG_UNIX=y
 CONFIG_UNIX_DIAG=m
+CONFIG_USER_NS=y
Signature · 1 line
2.53.0
author maintainer
#278 lore raw

PATCH 278/744 selftests: openvswitch: add config file

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Matthieu Baerts (NGI0) <[email protected]>

[ Upstream commit 441a820ccef9af80a9ac5a4c85b9c396e595967c ]

The kselftests doc mentions that a config file should be present "if a test needs specific kernel config options enabled". This selftest requires some kernel config, but no config file was provided.

We could say that a sub-target could use the parent's config file, but the kselftests doc doesn't mention anything about that. Plus the net/openvswitch target is the only net target without a config file.

Here is a new config file, which is a trimmed version of the net one, with hopefully the minimal required kconfig on top of 'make defconfig'.

The Fixes tag points to the introduction of the net/openvswitch target, just to help validating this target on stable kernels.

---
 tools/testing/selftests/net/openvswitch/config | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)
 create mode 100644 tools/testing/selftests/net/openvswitch/config
tools/testing/selftests/net/openvswitch/config new file +16 -0
new file mode 100644
index 00000000000000..c659749cd086c7
@@ -0,0 +1,16 @@
+CONFIG_GENEVE=m
+CONFIG_INET_DIAG=y
+CONFIG_IPV6=y
+CONFIG_NETFILTER=y
+CONFIG_NET_IPGRE=m
+CONFIG_NET_IPGRE_DEMUX=m
+CONFIG_NF_CONNTRACK=m
+CONFIG_NF_CONNTRACK_OVS=y
+CONFIG_OPENVSWITCH=m
+CONFIG_OPENVSWITCH_GENEVE=m
+CONFIG_OPENVSWITCH_GRE=m
+CONFIG_OPENVSWITCH_VXLAN=m
+CONFIG_PSAMPLE=m
+CONFIG_VETH=y
+CONFIG_VLAN_8021Q=y
+CONFIG_VXLAN=m
Signature · 1 line
2.53.0
author maintainer
#279 lore raw

PATCH 279/744 selftests: ovpn: add IPV6 and VETH configs

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Matthieu Baerts (NGI0) <[email protected]>

[ Upstream commit 90c792681a3732caaf7bf5bc435877736baf591a ]

They are required to run the selftests:

- Tests are executed in v4 and v6.

- Virtual Ethernet are used between the different netns.

This has not been seen on NIPA before, because the 'ovpn' tests are executed with the 'tcp_ao' ones, merging their config files. These two kernel config are present in tools/testing/selftests/net/tcp_ao/config.

This issue is visible when only the ovpn config is used on top of the default one. This is the recommended way to execute selftest targets.

Fixes 959bc330a439 ("testing/selftests: add test tool and scripts for ovpn module")
---
 tools/testing/selftests/net/ovpn/config | 2 ++
 1 file changed, 2 insertions(+)
tools/testing/selftests/net/ovpn/config +2 -0
index d6cf033d555e70..6b424762e46ee3 100644
@@ -4,6 +4,7 @@ CONFIG_CRYPTO_CHACHA20POLY1305=y
 CONFIG_CRYPTO_GCM=y
 CONFIG_DST_CACHE=y
 CONFIG_INET=y
+CONFIG_IPV6=y
 CONFIG_NET=y
 CONFIG_NETFILTER=y
 CONFIG_NET_UDP_TUNNEL=y
@@ -11,3 +12,4 @@ CONFIG_NF_TABLES=m
 CONFIG_NF_TABLES_INET=y
 CONFIG_OVPN=m
 CONFIG_STREAM_PARSER=y
+CONFIG_VETH=y
Signature · 1 line
2.53.0
author maintainer
#280 lore raw

PATCH 280/744 selftests: ovpn: increase timeout

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Matthieu Baerts (NGI0) <[email protected]>

[ Upstream commit 61ac7049aaa86ae044e8a5b94d852218163d5bf8 ]

The default timeout is 45 seconds, that's too low for a few ovpn tests.

Indeed, these tests can take up to 50 seconds with some debug kernel config on NIPA. Set a timeout to 90 seconds, just to be on the safe side.

Note that the Fixes tag here points to the introduction of the ovpn tests because I don't know when they started to take more than 45 seconds. That's OK because a timeout of 1.5 minutes is not exaggerated.

Fixes 959bc330a439 ("testing/selftests: add test tool and scripts for ovpn module")
---
 tools/testing/selftests/net/ovpn/settings | 1 +
 1 file changed, 1 insertion(+)
 create mode 100644 tools/testing/selftests/net/ovpn/settings
tools/testing/selftests/net/ovpn/settings new file +1 -0
new file mode 100644
index 00000000000000..ba4d85f74cd6b9
@@ -0,0 +1 @@
+timeout=90
Signature · 1 line
2.53.0
author maintainer
#281 lore raw

PATCH 281/744 selftests: drv-net: increase timeout

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Matthieu Baerts (NGI0) <[email protected]>

[ Upstream commit 3529d75d67411497341cd804a045185d6035dff2 ]

The default timeout is 45 seconds, that's too low for the xdp.py test.

Indeed, this test can take up to 3 minutes with some debug kernel config on NIPA. Set a timeout to 6 minutes, just to be on the safe side.

Note that the Fixes tag here points to the introduction of the xdp.py test because I don't know when this test started to take more than 45 seconds. That's OK because a timeout of 6 minutes is not exaggerated.

Fixes 1cbcb1b28b26 ("selftests: drv-net: Test XDP_PASS/DROP support")
---
 tools/testing/selftests/drivers/net/settings | 1 +
 1 file changed, 1 insertion(+)
 create mode 100644 tools/testing/selftests/drivers/net/settings
tools/testing/selftests/drivers/net/settings new file +1 -0
new file mode 100644
index 00000000000000..eef533824a3c19
@@ -0,0 +1 @@
+timeout=360
Signature · 1 line
2.53.0
author maintainer
#282 lore raw

PATCH 282/744 selftests: drv-net: convert so_txtime to drv-net

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Willem de Bruijn <[email protected]>

[ Upstream commit 5c6baef3885c0e16e63d432fe31aa6f7e98e18dc ]

In preparation for extending to pacing hardware offload, convert the so_txtime.sh test to a drv-net test that can be run against netdevsim and real hardware.

Also update so_txtime.c to not exit on first failure, but run to completion and report exit code there. This helps with debugging unexpected results, especially when processing multiple packets, as happens in the "reverse_order" testcase.

----

v6 -> v7

- update test to use new argument expect_fail
- v6 received Reviewed-by, but dropped due to above (minor) change

v5 -> v6

- fix order in tools/testing/selftests/drivers/net/config

v4 -> v5

- move qdisc setup/restore into each test
- add tc to utils.py (separate patch)
- test expected failure (separate patch)
- fix pylint
- convert fail to pass for timing errors if KSFT_MACHINE_SLOW (cmd does not special case KSFT_SKIP process returncode yet)

Responses to sashiko review

- The test converts per packet failure to errors, to continue testing other packets, but other error() cases are not in scope.
- The test starts sender and receiver at an absolute future time, like the original test. This assumes ~msec scale sync'ed clocks.
- The tc qdisc replace command works fine with noqueue. Tested manually.

v3 -> v4

- restore original qdisc after test
- drop unnecessary underscore in tap test names

v2 -> v3

- Makefile: so_txtime from YNL_GEN_FILES to TEST_GEN_FILES (Sashiko, NIPA)

v1 -> v2
- move so_txtime.c for net/lib to drivers/net (Jakub)
- fix drivers/net/config order (Jakub)
- detect passing when failure is expected (Jakub, Sashiko)
- pass pylint --disable=R (Jakub)
- only call ksft_run once (Jakub)
- do not sleep if waiting time is negative (Sashiko)
- add \n when converting error() to fprintf() (Sashiko)
- 4 space indentation, instead of 2 space
- increase sync delay from 100 to 200ms, to fix rare vng flakes

Stable-dep-of: c25dd7439f84 ("selftests: drv-net: add missing kconfig for psp.py")

---
 .../testing/selftests/drivers/net/.gitignore  |   1 +
 tools/testing/selftests/drivers/net/Makefile  |   2 +
 tools/testing/selftests/drivers/net/config    |   2 +
 .../selftests/{ => drivers}/net/so_txtime.c   |  25 +++-
 .../selftests/drivers/net/so_txtime.py        |  96 +++++++++++++++
 tools/testing/selftests/net/.gitignore        |   1 -
 tools/testing/selftests/net/Makefile          |   2 -
 tools/testing/selftests/net/so_txtime.sh      | 110 ------------------
 8 files changed, 121 insertions(+), 118 deletions(-)
 rename tools/testing/selftests/{ => drivers}/net/so_txtime.c (96%)
 create mode 100755 tools/testing/selftests/drivers/net/so_txtime.py
 delete mode 100755 tools/testing/selftests/net/so_txtime.sh
tools/testing/selftests/drivers/net/.gitignore +1 -0
index 585ecb4d5dc42e..e5314ce4bb2dea 100644
@@ -1,3 +1,4 @@
 # SPDX-License-Identifier: GPL-2.0-only
 napi_id_helper
 psp_responder
+so_txtime
tools/testing/selftests/drivers/net/Makefile +2 -0
index b72080c6d06b01..d5bf4cb638a8c4 100644
@@ -7,6 +7,7 @@ TEST_INCLUDES := $(wildcard lib/py/*.py) \
 
 TEST_GEN_FILES := \
 	napi_id_helper \
+	so_txtime \
 # end of TEST_GEN_FILES
 
 TEST_PROGS := \
@@ -21,6 +22,7 @@ TEST_PROGS := \
 	queues.py \
 	ring_reconfig.py \
 	shaper.py \
+	so_txtime.py \
 	stats.py \
 	xdp.py \
 # end of TEST_PROGS
tools/testing/selftests/drivers/net/config +2 -0
index fd16994366f4ba..2309109a94ecea 100644
@@ -8,5 +8,7 @@ CONFIG_NETCONSOLE=m
 CONFIG_NETCONSOLE_DYNAMIC=y
 CONFIG_NETCONSOLE_EXTENDED_LOG=y
 CONFIG_NETDEVSIM=m
+CONFIG_NET_SCH_ETF=m
+CONFIG_NET_SCH_FQ=m
 CONFIG_VLAN_8021Q=m
 CONFIG_XDP_SOCKETS=y
tools/testing/selftests/drivers/net/so_txtime.c renamed from tools/testing/selftests/net/so_txtime.c +20 -5
similarity index 96%
rename from tools/testing/selftests/net/so_txtime.c
rename to tools/testing/selftests/drivers/net/so_txtime.c
index b76df1efc2efc0..b6930883569b0a 100644
@@ -33,6 +33,8 @@
 #include <unistd.h>
 #include <poll.h>
 
+#include "kselftest.h"
+
 static int	cfg_clockid	= CLOCK_TAI;
 static uint16_t	cfg_port	= 8000;
 static int	cfg_variance_us	= 4000;
@@ -43,6 +45,8 @@ static bool	cfg_rx;
 static uint64_t glob_tstart;
 static uint64_t tdeliver_max;
 
+static int errors;
+
 /* encode one timed transmission (of a 1B payload) */
 struct timed_send {
 	char	data;
@@ -131,13 +135,15 @@ static void do_recv_one(int fdr, struct timed_send *ts)
 	fprintf(stderr, "payload:%c delay:%lld expected:%lld (us)\n",
 			rbuf[0], (long long)tstop, (long long)texpect);
 
-	if (rbuf[0] != ts->data)
-		error(1, 0, "payload mismatch. expected %c", ts->data);
+	if (rbuf[0] != ts->data) {
+		fprintf(stderr, "payload mismatch. expected %c\n", ts->data);
+		errors++;
+	}
 
 	if (llabs(tstop - texpect) > cfg_variance_us) {
 		fprintf(stderr, "exceeds variance (%d us)\n", cfg_variance_us);
 		if (!getenv("KSFT_MACHINE_SLOW"))
-			exit(1);
+			errors++;
 	}
 }
 
@@ -255,8 +261,12 @@ static void start_time_wait(void)
 		return;
 
 	now = gettime_ns(CLOCK_REALTIME);
-	if (cfg_start_time_ns < now)
+	if (cfg_start_time_ns < now) {
+		fprintf(stderr, "FAIL: start time already passed\n");
+		if (!getenv("KSFT_MACHINE_SLOW"))
+			errors++;
 		return;
+	}
 
 	err = usleep((cfg_start_time_ns - now) / 1000);
 	if (err)
@@ -513,5 +523,10 @@ int main(int argc, char **argv)
 	else
 		do_test_tx((void *)&cfg_src_addr, cfg_alen);
 
-	return 0;
+	if (errors) {
+		fprintf(stderr, "FAIL: %d errors\n", errors);
+		return KSFT_FAIL;
+	}
+
+	return KSFT_PASS;
 }
tools/testing/selftests/drivers/net/so_txtime.py new file +96 -0

96 lines not on this page · raw message

tools/testing/selftests/net/.gitignore +0 -1
index 97ad4d551d44d0..02ad4c99a2b49f 100644
@@ -40,7 +40,6 @@ skf_net_off
 socket
 so_incoming_cpu
 so_netns_cookie
-so_txtime
 so_rcv_listener
 stress_reuseport_listen
 tap
tools/testing/selftests/net/Makefile +0 -2
index 2ed7d803eb5484..2e4ff5b34f4cb4 100644
@@ -83,7 +83,6 @@ TEST_PROGS := \
 	rxtimestamp.sh \
 	sctp_vrf.sh \
 	skf_net_off.sh \
-	so_txtime.sh \
 	srv6_end_dt46_l3vpn_test.sh \
 	srv6_end_dt4_l3vpn_test.sh \
 	srv6_end_dt6_l3vpn_test.sh \
@@ -159,7 +158,6 @@ TEST_GEN_FILES := \
 	skf_net_off \
 	so_netns_cookie \
 	so_rcv_listener \
-	so_txtime \
 	socket \
 	stress_reuseport_listen \
 	tcp_fastopen_backup_key \
tools/testing/selftests/net/so_txtime.sh deleted +0 -110

110 lines not on this page · raw message

Signature · 1 line
2.53.0
author maintainer
#283 lore raw

PATCH 283/744 gtp: check skb_pull_data() return in gtp1u_send_echo_resp()

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Xiang Mei (Microsoft) <[email protected]>

[ Upstream commit cd170f051dba9ac146fabcd1b91726487c0cb9fa ]

gtp1u_send_echo_resp() ignores skb_pull_data()'s return value. Its caller gtp1u_udp_encap_recv() only guarantees 16 bytes (udphdr + gtp1_header), but the pull requests 20 (gtp1_header_long + udphdr). For a 16-19 byte echo request the pull fails and returns NULL without advancing skb->data; execution continues, and the following skb_push() plus the IP header pushed by iptunnel_xmit() move skb->data below skb->head, tripping skb_under_panic().

Fix it by dropping the packet when skb_pull_data() fails.

skbuff: skb_under_panic: ...
kernel BUG at net/core/skbuff.c:214!

  Call Trace:
   skb_push (net/core/skbuff.c:2648)
   iptunnel_xmit (net/ipv4/ip_tunnel_core.c:82)
   gtp_encap_recv (drivers/net/gtp.c:701 drivers/net/gtp.c:808 drivers/net/gtp.c:920)
   udp_queue_rcv_one_skb (net/ipv4/udp.c:2388)
   ...
  Kernel panic - not syncing: Fatal exception in interrupt
---
 drivers/net/gtp.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)
drivers/net/gtp.c +3 -2
index c0e38878af51b3..9a12cc53da0021 100644
@@ -669,8 +669,9 @@ static int gtp1u_send_echo_resp(struct gtp_dev *gtp, struct sk_buff *skb)
 		return -1;
 
 	/* pull GTP and UDP headers */
-	skb_pull_data(skb,
-		      sizeof(struct gtp1_header_long) + sizeof(struct udphdr));
+	if (!skb_pull_data(skb, sizeof(struct gtp1_header_long) +
+				sizeof(struct udphdr)))
+		return -1;
 
 	gtp_pkt = skb_push(skb, sizeof(struct gtp1u_packet));
 	memset(gtp_pkt, 0, sizeof(struct gtp1u_packet));
Signature · 1 line
2.53.0
author maintainer
#284 lore raw

PATCH 284/744 nexthop: initialize extack in nh_res_bucket_migrate()

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Xiang Mei (Microsoft) <[email protected]>

[ Upstream commit 6347c5314cee49f364aaf2e40ff15415a57a116e ]

nh_res_bucket_migrate() passes an uninitialized netlink_ext_ack to call_nexthop_res_bucket_notifiers(). When nh_notifier_res_bucket_info_init() fails (e.g. the kzalloc returns -ENOMEM), the error is propagated back before any notifier sets extack._msg, and the error path formats the stale pointer with pr_err_ratelimited("%s\n", extack._msg). With CONFIG_INIT_STACK_NONE this dereferences uninitialized stack memory:

  Oops: general protection fault, probably for non-canonical address ...
  KASAN: maybe wild-memory-access in range [...]
  RIP: 0010:string (lib/vsprintf.c:730)
   vsnprintf (lib/vsprintf.c:2945)
   _printk (kernel/printk/printk.c:2504)
   nh_res_bucket_migrate (net/ipv4/nexthop.c:1816)
   nh_res_table_upkeep (net/ipv4/nexthop.c:1866)
   rtm_new_nexthop (net/ipv4/nexthop.c:3323)
   rtnetlink_rcv_msg (net/core/rtnetlink.c:7076)
   netlink_sendmsg (net/netlink/af_netlink.c:1900)
  Kernel panic - not syncing: Fatal exception

Zero-initialize extack so _msg is NULL on error paths that never set it.

Fixes 7c37c7e00411 ("nexthop: Implement notifiers for resilient nexthop groups")
Reported-by [email protected]
---
 net/ipv4/nexthop.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
net/ipv4/nexthop.c +1 -1
index f92fcc39fc4c80..dc95a3d6e899e0 100644
@@ -1788,8 +1788,8 @@ static bool nh_res_bucket_migrate(struct nh_res_table *res_table,
 				  bool notify_nl, bool force)
 {
 	struct nh_res_bucket *bucket = &res_table->nh_buckets[bucket_index];
+	struct netlink_ext_ack extack = {};
 	struct nh_grp_entry *new_nhge;
-	struct netlink_ext_ack extack;
 	int err;
 
 	new_nhge = list_first_entry_or_null(&res_table->uw_nh_entries,
Signature · 1 line
2.53.0
author maintainer
#285 lore raw

PATCH 285/744 tipc: fix infinite loop in __tipc_nl_compat_dumpit

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Helen Koike <[email protected]>

[ Upstream commit 22f8aa35964e8f2ab026578f45befc9605fd1b28 ]

cmd->dumpit callback can return a negative errno, causing an infinite loop due to the while(len) condition. As the loop never terminates, genl_mutex is never released, and other tasks waiting on it starve in D state.

Check dumpit's return value, propagate it and jump to err_out on error.

---
 net/tipc/netlink_compat.c | 4 ++++
 1 file changed, 4 insertions(+)
net/tipc/netlink_compat.c +4 -0
index 2a786c56c8c59e..d9a4f94ea2d43d 100644
@@ -221,6 +221,10 @@ static int __tipc_nl_compat_dumpit(struct tipc_nl_compat_cmd_dump *cmd,
 		int rem;
 
 		len = (*cmd->dumpit)(buf, &cb);
+		if (len < 0) {
+			err = len;
+			goto err_out;
+		}
 
 		nlmsg_for_each_msg(nlmsg, nlmsg_hdr(buf), len, rem) {
 			err = nlmsg_parse_deprecated(nlmsg, GENL_HDRLEN,
Signature · 1 line
2.53.0
author maintainer
#286 lore raw

PATCH 286/744 cifs: prevent readdir from changing file size due to stale directory metadata

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Frank Sorenson <[email protected]>

[ Upstream commit e8a8d54c2d508891c142a928fc7d298c4c8bd0dd ]

Windows Server's directory enumeration metadata lags behind the actual file size after a write+close or rename. A concurrent readdir() in the window between close() returning to userspace and stat() being called overwrites the correct cached i_size with the stale server value, causing stat() to return the wrong size.

Once _cifsFileInfo_put() removes the last writable handle from openFileList, is_size_safe_to_change() permits readdir to overwrite i_size. smb2_close_getattr() then stamps cifs_i->time = jiffies, making the corrupt cached value appear fresh to the next stat().

The existing check (see Fixes:) only blocked stale size updates while an active RW lease was held, not after the last writable handle closes.

Add cifsInodeInfo->time_last_write, written via smp_store_release() at writable close and on setattr/truncate. is_size_safe_to_change() checks is_inode_writable() first (acquiring open_file_lock), then rejects a readdir size update if time_last_write falls within acregmax jiffies. The spinlock release in _cifsFileInfo_put() forms a store-release barrier that pairs with the spin_lock() (load-acquire) in is_inode_writable(), ensuring the subsequent smp_load_acquire() on time_last_write observes any update from a concurrent close(). When a size update is rejected and the server value differs from the cached one, cifs_i->time is cleared to force a fresh QUERY_INFO on the next stat().

readdir is also blocked from changing i_size while writable handles are open or an RW lease is held, even on direct-IO mounts.

For deferred close (closetimeo > 0), time_last_write is refreshed at the actual server close in smb2_deferred_work_close() and in the cifs_close_deferred_file*() drain paths invoked by lease/oplock breaks and tcon teardown, anchoring the protection window to the real close time rather than the earlier userspace close.

time_last_write == 0 skips the time_before() check to avoid false positives near boot on 32-bit systems where jiffies starts close to INITIAL_JIFFIES.

Does not reproduce against Samba or with actimeo=0.

Fixes e4b61f3b1c67 ("cifs: prevent updating file size from server if we have a read/write lease")
---
 fs/smb/client/cifsfs.c   |  1 +
 fs/smb/client/cifsglob.h |  1 +
 fs/smb/client/file.c     | 69 +++++++++++++++++++++++++++++++++++++---
 fs/smb/client/inode.c    |  6 ++++
 fs/smb/client/misc.c     | 27 ++++++++++++++--
 5 files changed, 96 insertions(+), 8 deletions(-)
fs/smb/client/cifsfs.c +1 -0
index d6c30f8552e081..95845c87745173 100644
@@ -438,6 +438,7 @@ cifs_alloc_inode(struct super_block *sb)
 		return NULL;
 	cifs_inode->cifsAttrs = ATTR_ARCHIVE;	/* default */
 	cifs_inode->time = 0;
+	cifs_inode->time_last_write = 0;
 	/*
 	 * Until the file is open and we have gotten oplock info back from the
 	 * server, can not assume caching of file data or metadata.
fs/smb/client/cifsglob.h +1 -0
index 82e0adc1dabd05..c755d314a8193b 100644
@@ -1562,6 +1562,7 @@ struct cifsInodeInfo {
 	spinlock_t writers_lock;
 	unsigned int writers;		/* Number of writers on this inode */
 	unsigned long time;		/* jiffies of last update of inode */
+	unsigned long time_last_write;	/* jiffies of last writable close or truncate */
 	u64  uniqueid;			/* server inode number */
 	u64  createtime;		/* creation time on server */
 	__u8 lease_key[SMB2_LEASE_KEY_SIZE];	/* lease key for this inode */
fs/smb/client/file.c +64 -5

102 lines not on this page · raw message

fs/smb/client/inode.c +6 -0
index 826d36ed13ec92..04470803e78a99 100644
@@ -237,6 +237,8 @@ cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr,
 	if (is_size_safe_to_change(cifs_i, fattr->cf_eof, from_readdir)) {
 		i_size_write(inode, fattr->cf_eof);
 		inode->i_blocks = CIFS_INO_BLOCKS(fattr->cf_bytes);
+	} else if (from_readdir && i_size_read(inode) != fattr->cf_eof) {
+		cifs_i->time = 0;
 	}
 
 	if (S_ISLNK(fattr->cf_mode) && fattr->cf_symlink_target) {
@@ -3269,6 +3271,8 @@ cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs)
 
 	if ((attrs->ia_valid & ATTR_SIZE) &&
 	    attrs->ia_size != i_size_read(inode)) {
+		/* Pairs with smp_load_acquire() in is_size_safe_to_change(). */
+		smp_store_release(&cifsInode->time_last_write, jiffies);
 		truncate_setsize(inode, attrs->ia_size);
 		netfs_resize_file(&cifsInode->netfs, attrs->ia_size, true);
 		fscache_resize_cookie(cifs_inode_cookie(inode), attrs->ia_size);
@@ -3470,6 +3474,8 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs)
 
 	if ((attrs->ia_valid & ATTR_SIZE) &&
 	    attrs->ia_size != i_size_read(inode)) {
+		/* Pairs with smp_load_acquire() in is_size_safe_to_change(). */
+		smp_store_release(&cifsInode->time_last_write, jiffies);
 		truncate_setsize(inode, attrs->ia_size);
 		netfs_resize_file(&cifsInode->netfs, attrs->ia_size, true);
 		fscache_resize_cookie(cifs_inode_cookie(inode), attrs->ia_size);
fs/smb/client/misc.c +24 -3
index f8bc1b8d3fdeae..8afa746ee19e0d 100644
@@ -520,7 +520,14 @@ cifs_close_deferred_file(struct cifsInodeInfo *cifs_inode)
 	spin_unlock(&cifs_inode->open_file_lock);
 
 	list_for_each_entry_safe(tmp_list, tmp_next_list, &file_head, list) {
-		_cifsFileInfo_put(tmp_list->cfile, false, false);
+		struct cifsFileInfo *cfile = tmp_list->cfile;
+
+		if (OPEN_FMODE(cfile->f_flags) & FMODE_WRITE) {
+			/* Pairs with smp_load_acquire() in is_size_safe_to_change(). */
+			smp_store_release(&CIFS_I(d_inode(cfile->dentry))->time_last_write,
+					  jiffies);
+		}
+		_cifsFileInfo_put(cfile, false, false);
 		list_del(&tmp_list->list);
 		kfree(tmp_list);
 	}
@@ -553,7 +560,14 @@ cifs_close_all_deferred_files(struct cifs_tcon *tcon)
 	spin_unlock(&tcon->open_file_lock);
 
 	list_for_each_entry_safe(tmp_list, tmp_next_list, &file_head, list) {
-		_cifsFileInfo_put(tmp_list->cfile, true, false);
+		struct cifsFileInfo *cfile = tmp_list->cfile;
+
+		if (OPEN_FMODE(cfile->f_flags) & FMODE_WRITE) {
+			/* Pairs with smp_load_acquire() in is_size_safe_to_change(). */
+			smp_store_release(&CIFS_I(d_inode(cfile->dentry))->time_last_write,
+					  jiffies);
+		}
+		_cifsFileInfo_put(cfile, true, false);
 		list_del(&tmp_list->list);
 		kfree(tmp_list);
 	}
@@ -622,7 +636,14 @@ void cifs_close_deferred_file_under_dentry(struct cifs_tcon *tcon,
 	spin_unlock(&tcon->open_file_lock);
 
 	list_for_each_entry_safe(tmp_list, tmp_next_list, &file_head, list) {
-		_cifsFileInfo_put(tmp_list->cfile, true, false);
+		struct cifsFileInfo *cfile = tmp_list->cfile;
+
+		if (OPEN_FMODE(cfile->f_flags) & FMODE_WRITE) {
+			/* Pairs with smp_load_acquire() in is_size_safe_to_change(). */
+			smp_store_release(&CIFS_I(d_inode(cfile->dentry))->time_last_write,
+					  jiffies);
+		}
+		_cifsFileInfo_put(cfile, true, false);
 		list_del(&tmp_list->list);
 		kfree(tmp_list);
 	}
Signature · 1 line
2.53.0
#287 lore raw

Re: [PATCH 7.1 286/744] cifs: prevent readdir from changing file size due to stale directory metadata

Quoted 7 lines · Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Frank Sorenson <[email protected]>

[ Upstream commit e8a8d54c2d508891c142a928fc7d298c4c8bd0dd ]

These two allegedly fix the above:
ecababf08905 ("cifs: fix time_last_write stamp placement in setattr/truncate paths") 0e3ea5445c22 ("cifs: consolidate time_last_write stamp into _cifsFileInfo_put()")

thanks,

Signature · 2 lines
js
suse labs
author maintainer
#288 lore raw

Re: [PATCH 7.1 286/744] cifs: prevent readdir from changing file size due to stale directory metadata

Quoted 7 lines · Jiri Slaby
Quoted 6 lines · Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Frank Sorenson <[email protected]>

Quoted 7 lines
Quoted 1 line

[ Upstream commit e8a8d54c2d508891c142a928fc7d298c4c8bd0dd ]

These two allegedly fix the above:
ecababf08905 ("cifs: fix time_last_write stamp placement in setattr/truncate paths")
0e3ea5445c22 ("cifs: consolidate time_last_write stamp into _cifsFileInfo_put()")

I don't scan Linus's tree (i.e. stuff before a -rc) so I missed these. Thanks for that, I'll go queue them up now.

greg k-h

author maintainer
#289 lore raw

PATCH 287/744 cifs: fix cifsFileInfo leak on kmalloc failure in deferred close drain paths

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Frank Sorenson <[email protected]>

[ Upstream commit c2f2e83e3bbc5483730fd4ee903182761f1ae50f ]

In cifs_close_deferred_file(), cifs_close_all_deferred_files(), and cifs_close_deferred_file_under_dentry(), when a pending deferred close is cancelled via cancel_delayed_work(), the subsequent kmalloc_obj() to add the file to the local processing list may fail under memory pressure. The loop breaks immediately, but the cancelled work is no longer pending (it would have called _cifsFileInfo_put()), and the cfile is never added to file_head for processing. The cifsFileInfo reference and the open server handle both leak.

Fix by saving the cfile that failed allocation in a local variable, breaking as before, and calling _cifsFileInfo_put() on it after releasing the lock. Any files later in the iteration are unaffected since their deferred work is still pending and will fire normally.

Fixes e3fc065682eb ("cifs: Deferred close performance improvements")
---
 fs/smb/client/misc.c | 45 ++++++++++++++++++++++++++++++++++++++------
 1 file changed, 39 insertions(+), 6 deletions(-)
fs/smb/client/misc.c +39 -6
index 8afa746ee19e0d..ce9927b666af7a 100644
@@ -493,7 +493,7 @@ cifs_del_deferred_close(struct cifsFileInfo *cfile)
 void
 cifs_close_deferred_file(struct cifsInodeInfo *cifs_inode)
 {
-	struct cifsFileInfo *cfile = NULL;
+	struct cifsFileInfo *cfile = NULL, *failed_cfile = NULL;
 	struct file_list *tmp_list, *tmp_next_list;
 	LIST_HEAD(file_head);
 
@@ -510,8 +510,10 @@ cifs_close_deferred_file(struct cifsInodeInfo *cifs_inode)
 
 				tmp_list = kmalloc_obj(struct file_list,
 						       GFP_ATOMIC);
-				if (tmp_list == NULL)
+				if (tmp_list == NULL) {
+					failed_cfile = cfile;
 					break;
+				}
 				tmp_list->cfile = cfile;
 				list_add_tail(&tmp_list->list, &file_head);
 			}
@@ -519,6 +521,15 @@ cifs_close_deferred_file(struct cifsInodeInfo *cifs_inode)
 	}
 	spin_unlock(&cifs_inode->open_file_lock);
 
+	if (failed_cfile) {
+		if (OPEN_FMODE(failed_cfile->f_flags) & FMODE_WRITE) {
+			/* Pairs with smp_load_acquire() in is_size_safe_to_change(). */
+			smp_store_release(&CIFS_I(d_inode(failed_cfile->dentry))->time_last_write,
+					  jiffies);
+		}
+		_cifsFileInfo_put(failed_cfile, false, false);
+	}
+
 	list_for_each_entry_safe(tmp_list, tmp_next_list, &file_head, list) {
 		struct cifsFileInfo *cfile = tmp_list->cfile;
 
@@ -536,7 +547,7 @@ cifs_close_deferred_file(struct cifsInodeInfo *cifs_inode)
 void
 cifs_close_all_deferred_files(struct cifs_tcon *tcon)
 {
-	struct cifsFileInfo *cfile;
+	struct cifsFileInfo *cfile, *failed_cfile = NULL;
 	struct file_list *tmp_list, *tmp_next_list;
 	LIST_HEAD(file_head);
 
@@ -550,8 +561,10 @@ cifs_close_all_deferred_files(struct cifs_tcon *tcon)
 
 				tmp_list = kmalloc_obj(struct file_list,
 						       GFP_ATOMIC);
-				if (tmp_list == NULL)
+				if (tmp_list == NULL) {
+					failed_cfile = cfile;
 					break;
+				}
 				tmp_list->cfile = cfile;
 				list_add_tail(&tmp_list->list, &file_head);
 			}
@@ -559,6 +572,15 @@ cifs_close_all_deferred_files(struct cifs_tcon *tcon)
 	}
 	spin_unlock(&tcon->open_file_lock);
 
+	if (failed_cfile) {
+		if (OPEN_FMODE(failed_cfile->f_flags) & FMODE_WRITE) {
+			/* Pairs with smp_load_acquire() in is_size_safe_to_change(). */
+			smp_store_release(&CIFS_I(d_inode(failed_cfile->dentry))->time_last_write,
+					  jiffies);
+		}
+		_cifsFileInfo_put(failed_cfile, true, false);
+	}
+
 	list_for_each_entry_safe(tmp_list, tmp_next_list, &file_head, list) {
 		struct cifsFileInfo *cfile = tmp_list->cfile;
 
@@ -614,7 +636,7 @@ void cifs_close_deferred_file_under_dentry(struct cifs_tcon *tcon,
 					   struct dentry *dentry)
 {
 	struct file_list *tmp_list, *tmp_next_list;
-	struct cifsFileInfo *cfile;
+	struct cifsFileInfo *cfile, *failed_cfile = NULL;
 	LIST_HEAD(file_head);
 
 	spin_lock(&tcon->open_file_lock);
@@ -627,14 +649,25 @@ void cifs_close_deferred_file_under_dentry(struct cifs_tcon *tcon,
 			spin_unlock(&CIFS_I(d_inode(cfile->dentry))->deferred_lock);
 
 			tmp_list = kmalloc_obj(struct file_list, GFP_ATOMIC);
-			if (tmp_list == NULL)
+			if (tmp_list == NULL) {
+				failed_cfile = cfile;
 				break;
+			}
 			tmp_list->cfile = cfile;
 			list_add_tail(&tmp_list->list, &file_head);
 		}
 	}
 	spin_unlock(&tcon->open_file_lock);
 
+	if (failed_cfile) {
+		if (OPEN_FMODE(failed_cfile->f_flags) & FMODE_WRITE) {
+			/* Pairs with smp_load_acquire() in is_size_safe_to_change(). */
+			smp_store_release(&CIFS_I(d_inode(failed_cfile->dentry))->time_last_write,
+					  jiffies);
+		}
+		_cifsFileInfo_put(failed_cfile, true, false);
+	}
+
 	list_for_each_entry_safe(tmp_list, tmp_next_list, &file_head, list) {
 		struct cifsFileInfo *cfile = tmp_list->cfile;
 
Signature · 1 line
2.53.0
author maintainer
#290 lore raw

PATCH 288/744 ppp: annotate concurrent dev->stats accesses

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Eric Dumazet <[email protected]>

[ Upstream commit ba712ecfd942b68b21a4b0a5daaf72f6616cc66d ]

dev->stats fields can be updated concurrently from multiple CPUs without synchronization.

Use DEV_STATS_INC() for stats increments and DEV_STATS_READ() when reading dev->stats in ppp_get_stats64() and ppp_get_stats() to avoid data races.

---
 drivers/net/ppp/ppp_generic.c | 32 ++++++++++++++++----------------
 1 file changed, 16 insertions(+), 16 deletions(-)
drivers/net/ppp/ppp_generic.c +16 -16
index 717c1d3aa95380..ef54e0a0462a17 100644
@@ -1482,7 +1482,7 @@ ppp_start_xmit(struct sk_buff *skb, struct net_device *dev)
 
  outf:
 	kfree_skb(skb);
-	++dev->stats.tx_dropped;
+	DEV_STATS_INC(dev, tx_dropped);
 	return NETDEV_TX_OK;
 }
 
@@ -1532,11 +1532,11 @@ ppp_net_siocdevprivate(struct net_device *dev, struct ifreq *ifr,
 static void
 ppp_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats64)
 {
-	stats64->rx_errors        = dev->stats.rx_errors;
-	stats64->tx_errors        = dev->stats.tx_errors;
-	stats64->rx_dropped       = dev->stats.rx_dropped;
-	stats64->tx_dropped       = dev->stats.tx_dropped;
-	stats64->rx_length_errors = dev->stats.rx_length_errors;
+	stats64->rx_errors        = DEV_STATS_READ(dev, rx_errors);
+	stats64->tx_errors        = DEV_STATS_READ(dev, tx_errors);
+	stats64->rx_dropped       = DEV_STATS_READ(dev, rx_dropped);
+	stats64->tx_dropped       = DEV_STATS_READ(dev, tx_dropped);
+	stats64->rx_length_errors = DEV_STATS_READ(dev, rx_length_errors);
 	dev_fetch_sw_netstats(stats64, dev->tstats);
 }
 
@@ -1889,7 +1889,7 @@ ppp_prepare_tx_skb(struct ppp *ppp, struct sk_buff **pskb)
 
  drop:
 	kfree_skb(skb);
-	++ppp->dev->stats.tx_errors;
+	DEV_STATS_INC(ppp->dev, tx_errors);
 	return 1;
 }
 
@@ -2156,7 +2156,7 @@ static int ppp_mp_explode(struct ppp *ppp, struct sk_buff *skb)
  err_linearize:
 	if (ppp->debug & 1)
 		netdev_err(ppp->dev, "PPP: no memory (fragment)\n");
-	++ppp->dev->stats.tx_errors;
+	DEV_STATS_INC(ppp->dev, tx_errors);
 	++ppp->nxseq;
 	return 1;	/* abandon the frame */
 }
@@ -2329,7 +2329,7 @@ ppp_input(struct ppp_channel *chan, struct sk_buff *skb)
 	if (!ppp_decompress_proto(skb)) {
 		kfree_skb(skb);
 		if (ppp) {
-			++ppp->dev->stats.rx_length_errors;
+			DEV_STATS_INC(ppp->dev, rx_length_errors);
 			ppp_receive_error(ppp);
 		}
 		goto done;
@@ -2391,7 +2391,7 @@ ppp_receive_frame(struct ppp *ppp, struct sk_buff *skb, struct channel *pch)
 static void
 ppp_receive_error(struct ppp *ppp)
 {
-	++ppp->dev->stats.rx_errors;
+	DEV_STATS_INC(ppp->dev, rx_errors);
 	if (ppp->vj)
 		slhc_toss(ppp->vj);
 }
@@ -2658,7 +2658,7 @@ ppp_receive_mp_frame(struct ppp *ppp, struct sk_buff *skb, struct channel *pch)
 	 */
 	if (seq_before(seq, ppp->nextseq)) {
 		kfree_skb(skb);
-		++ppp->dev->stats.rx_dropped;
+		DEV_STATS_INC(ppp->dev, rx_dropped);
 		ppp_receive_error(ppp);
 		return;
 	}
@@ -2694,7 +2694,7 @@ ppp_receive_mp_frame(struct ppp *ppp, struct sk_buff *skb, struct channel *pch)
 		if (pskb_may_pull(skb, 2))
 			ppp_receive_nonmp_frame(ppp, skb);
 		else {
-			++ppp->dev->stats.rx_length_errors;
+			DEV_STATS_INC(ppp->dev, rx_length_errors);
 			kfree_skb(skb);
 			ppp_receive_error(ppp);
 		}
@@ -2800,7 +2800,7 @@ ppp_mp_reconstruct(struct ppp *ppp)
 		if (lost == 0 && (PPP_MP_CB(p)->BEbits & E) &&
 		    (PPP_MP_CB(head)->BEbits & B)) {
 			if (len > ppp->mrru + 2) {
-				++ppp->dev->stats.rx_length_errors;
+				DEV_STATS_INC(ppp->dev, rx_length_errors);
 				netdev_printk(KERN_DEBUG, ppp->dev,
 					      "PPP: reconstructed packet"
 					      " is too long (%d)\n", len);
@@ -2855,7 +2855,7 @@ ppp_mp_reconstruct(struct ppp *ppp)
 					      "  missed pkts %u..%u\n",
 					      ppp->nextseq,
 					      PPP_MP_CB(head)->sequence-1);
-			++ppp->dev->stats.rx_dropped;
+			DEV_STATS_INC(ppp->dev, rx_dropped);
 			ppp_receive_error(ppp);
 		}
 
@@ -3322,8 +3322,8 @@ ppp_get_stats(struct ppp *ppp, struct ppp_stats *st)
 		st->p.ppp_opackets += tx_packets;
 		st->p.ppp_obytes += tx_bytes;
 	}
-	st->p.ppp_ierrors = ppp->dev->stats.rx_errors;
-	st->p.ppp_oerrors = ppp->dev->stats.tx_errors;
+	st->p.ppp_ierrors = DEV_STATS_READ(ppp->dev, rx_errors);
+	st->p.ppp_oerrors = DEV_STATS_READ(ppp->dev, tx_errors);
 	if (!vj)
 		return;
 	st->vj.vjs_packets = vj->sls_o_compressed + vj->sls_o_uncompressed;
Signature · 1 line
2.53.0
author maintainer
#291 lore raw

PATCH 289/744 wifi: mt76: mt7925: guard link STA in decap offload

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Guangshuo Li <[email protected]>

[ Upstream commit 96ea44f2269f30364cffa054ee3a87e595bef0d4 ]

mt7925_sta_set_decap_offload() iterates over the vif valid_links mask when updating decap offload state for an MLO station. The station may not have a link STA for every valid link of the vif, so mt792x_sta_to_link() can return NULL for a link that belongs to the vif but not to the station.

The function currently dereferences mlink before checking whether the link WCID is ready. If mlink is NULL, setting or clearing MT_WCID_FLAG_HDR_TRANS dereferences a NULL pointer.

Skip links without a station link before touching mlink->wcid.

Fixes b859ad65309a ("wifi: mt76: mt7925: add link handling in mt7925_sta_set_decap_offload")
---
 drivers/net/wireless/mediatek/mt76/mt7925/main.c | 3 +++
 1 file changed, 3 insertions(+)
drivers/net/wireless/mediatek/mt76/mt7925/main.c +3 -0
index 8765121b916a26..e7fb49d0715011 100644
@@ -1678,6 +1678,9 @@ static void mt7925_sta_set_decap_offload(struct ieee80211_hw *hw,
 		mconf = mt792x_vif_to_link(mvif, i);
 		mlink = mt792x_sta_to_link(msta, i);
 
+		if (!mlink)
+			continue;
+
 		if (enabled)
 			set_bit(MT_WCID_FLAG_HDR_TRANS, &mlink->wcid.flags);
 		else
Signature · 1 line
2.53.0
author maintainer
#292 lore raw

PATCH 290/744 wifi: mt76: mt7915: guard HE capability lookups

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Ruoyu Wang <[email protected]>

[ Upstream commit 8e9db062654a388d0fa587acbeeae68dd33eba41 ]

mt7915_mcu_bss_he_tlv() and mt7915_mcu_sta_bfer_tlv() both run after checking HE support, then dereference the HE PHY capability returned by mt76_connac_get_he_phy_cap(). That helper can return NULL when no capability entry matches the vif type.

Fetch the capability before appending the TLV and skip the HE-specific setup when no matching capability is available.

Fixes e6d557a78b60 ("mt76: mt7915: rely on mt76_connac_get_phy utilities")
---
 .../net/wireless/mediatek/mt76/mt7915/mcu.c    | 18 +++++++++++++-----
 1 file changed, 13 insertions(+), 5 deletions(-)
drivers/net/wireless/mediatek/mt76/mt7915/mcu.c +13 -5
index 318c3814946372..391c9167513075 100644
@@ -595,6 +595,8 @@ mt7915_mcu_bss_he_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
 	struct tlv *tlv;
 
 	cap = mt76_connac_get_he_phy_cap(phy->mt76, vif);
+	if (!cap)
+		return;
 
 	tlv = mt76_connac_mcu_add_tlv(skb, BSS_INFO_HE_BASIC, sizeof(*he));
 
@@ -1177,13 +1179,12 @@ mt7915_mcu_sta_bfer_vht(struct ieee80211_sta *sta, struct mt7915_phy *phy,
 }
 
 static void
-mt7915_mcu_sta_bfer_he(struct ieee80211_sta *sta, struct ieee80211_vif *vif,
-		       struct mt7915_phy *phy, struct sta_rec_bf *bf)
+mt7915_mcu_sta_bfer_he(struct ieee80211_sta *sta,
+		       const struct ieee80211_sta_he_cap *vc,
+		       struct sta_rec_bf *bf)
 {
 	struct ieee80211_sta_he_cap *pc = &sta->deflink.he_cap;
 	struct ieee80211_he_cap_elem *pe = &pc->he_cap_elem;
-	const struct ieee80211_sta_he_cap *vc =
-		mt76_connac_get_he_phy_cap(phy->mt76, vif);
 	const struct ieee80211_he_cap_elem *ve = &vc->he_cap_elem;
 	u16 mcs_map = le16_to_cpu(pc->he_mcs_nss_supp.rx_mcs_80);
 	u8 nss_mcs = mt7915_mcu_get_sta_nss(mcs_map);
@@ -1242,6 +1243,7 @@ mt7915_mcu_sta_bfer_tlv(struct mt7915_dev *dev, struct sk_buff *skb,
 {
 	struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv;
 	struct mt7915_phy *phy = mvif->phy;
+	const struct ieee80211_sta_he_cap *vc = NULL;
 	int tx_ant = hweight8(phy->mt76->chainmask) - 1;
 	struct sta_rec_bf *bf;
 	struct tlv *tlv;
@@ -1260,6 +1262,12 @@ mt7915_mcu_sta_bfer_tlv(struct mt7915_dev *dev, struct sk_buff *skb,
 	if (!ebf && !dev->ibf)
 		return;
 
+	if (sta->deflink.he_cap.has_he && ebf) {
+		vc = mt76_connac_get_he_phy_cap(phy->mt76, vif);
+		if (!vc)
+			return;
+	}
+
 	tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_BF, sizeof(*bf));
 	bf = (struct sta_rec_bf *)tlv;
 
@@ -1268,7 +1276,7 @@ mt7915_mcu_sta_bfer_tlv(struct mt7915_dev *dev, struct sk_buff *skb,
 	 * ht: iBF only, since mac80211 lacks of eBF support
 	 */
 	if (sta->deflink.he_cap.has_he && ebf)
-		mt7915_mcu_sta_bfer_he(sta, vif, phy, bf);
+		mt7915_mcu_sta_bfer_he(sta, vc, bf);
 	else if (sta->deflink.vht_cap.vht_supported)
 		mt7915_mcu_sta_bfer_vht(sta, phy, bf, ebf);
 	else if (sta->deflink.ht_cap.ht_supported)
Signature · 1 line
2.53.0
author maintainer
#293 lore raw

PATCH 291/744 wifi: mt76: connac: fix possible NULL-pointer deref in mt76_connac_mcu_uni_bss_he_tlv()

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Lorenzo Bianconi <[email protected]>

[ Upstream commit 2c1fb2335f5e3afb34f91bc07ecb63517c328090 ]

mt76_connac_get_he_phy_cap routine can theoretically return NULL so check cap pointer before dereferencing it.

---
 drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c | 2 ++
 1 file changed, 2 insertions(+)
drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c +2 -0
index 89bd52ea8bf704..ca290ccb6fd692 100644
@@ -1446,6 +1446,8 @@ mt76_connac_mcu_uni_bss_he_tlv(struct mt76_phy *phy, struct ieee80211_vif *vif,
 	struct bss_info_uni_he *he;
 
 	cap = mt76_connac_get_he_phy_cap(phy, vif);
+	if (!cap)
+		return;
 
 	he = (struct bss_info_uni_he *)tlv;
 	he->he_pe_duration = vif->bss_conf.htc_trig_based_pkt_ext;
Signature · 1 line
2.53.0
author maintainer
#294 lore raw

PATCH 292/744 wifi: mt76: mt7925: fix possible NULL-pointer deref in mt7925_mcu_bss_he_tlv()

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Lorenzo Bianconi <[email protected]>

[ Upstream commit 8d1b6738c1ab48c086b17e7994034aca94258931 ]

mt76_connac_get_he_phy_cap routine can theoretically return NULL so check cap pointer before dereferencing it.

---
 drivers/net/wireless/mediatek/mt76/mt7925/mcu.c | 2 ++
 1 file changed, 2 insertions(+)
drivers/net/wireless/mediatek/mt76/mt7925/mcu.c +2 -0
index 37cdf3e8a06706..4f20f62fb70a3c 100644
@@ -2694,6 +2694,8 @@ mt7925_mcu_bss_he_tlv(struct sk_buff *skb, struct ieee80211_bss_conf *link_conf,
 	struct tlv *tlv;
 
 	cap = mt76_connac_get_he_phy_cap(phy->mt76, link_conf->vif);
+	if (!cap)
+		return;
 
 	tlv = mt76_connac_mcu_add_tlv(skb, UNI_BSS_INFO_HE_BASIC, sizeof(*he));
 
Signature · 1 line
2.53.0
author maintainer
#295 lore raw

PATCH 293/744 wifi: mt76: mt7996: check pointer returned by mt76_connac_get_he_phy_cap()

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Lorenzo Bianconi <[email protected]>

[ Upstream commit e858cf6bf99880343348ff1e8c942aaff1d9d592 ]

mt76_connac_get_he_phy_cap routine can theoretically return NULL so check cap pointer before dereferencing it.

Fixes 98686cd21624c ("wifi: mt76: mt7996: add driver for MediaTek Wi-Fi 7 (802.11be) devices")
---
 drivers/net/wireless/mediatek/mt76/mt7996/mcu.c | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)
drivers/net/wireless/mediatek/mt76/mt7996/mcu.c +6 -3
index 2748bfeb479744..a6a21ddd17a2a0 100644
@@ -935,6 +935,8 @@ mt7996_mcu_bss_he_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
 	struct tlv *tlv;
 
 	cap = mt76_connac_get_he_phy_cap(phy->mt76, vif);
+	if (!cap)
+		return;
 
 	tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_HE_BASIC, sizeof(*he));
 
@@ -1851,17 +1853,18 @@ mt7996_mcu_sta_bfer_he(struct ieee80211_link_sta *link_sta,
 {
 	struct ieee80211_sta_he_cap *pc = &link_sta->he_cap;
 	struct ieee80211_he_cap_elem *pe = &pc->he_cap_elem;
-	const struct ieee80211_sta_he_cap *vc =
-		mt76_connac_get_he_phy_cap(phy->mt76, vif);
-	const struct ieee80211_he_cap_elem *ve = &vc->he_cap_elem;
 	u16 mcs_map = le16_to_cpu(pc->he_mcs_nss_supp.rx_mcs_80);
 	u8 nss_mcs = mt7996_mcu_get_sta_nss(mcs_map);
+	const struct ieee80211_he_cap_elem *ve;
+	const struct ieee80211_sta_he_cap *vc;
 	u8 snd_dim, sts;
 
+	vc = mt76_connac_get_he_phy_cap(phy->mt76, vif);
 	if (!vc)
 		return;
 
 	bf->tx_mode = MT_PHY_TYPE_HE_SU;
+	ve = &vc->he_cap_elem;
 
 	mt7996_mcu_sta_sounding_rate(bf, phy);
 
Signature · 1 line
2.53.0
author maintainer
#296 lore raw

PATCH 294/744 wifi: mt76: fix airoha_npu dependency tracking

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Arnd Bergmann <[email protected]>

[ Upstream commit 7cd57ff6c6263519e6e463cbc2e0898828a70c42 ]

There is a new build failure with MT7996E=m MT76_CORE=y and NET_AIROHA_NPU=m:

ld.lld: error: undefined symbol: airoha_npu_get
ld.lld: error: undefined symbol: airoha_npu_put

Quoted 2 lines
Quoted 2 lines
Quoted 2 lines

referenced by npu.c
drivers/net/wireless/mediatek/mt76/npu.o:(mt76_npu_init) in archive vmlinux.a

Fix this by reworking the dependency for the MT7996_NPU to only allow enabling that when mt76_core can link against the npu driver.

To make sure this gets caught more easily in the future when additional mt76 variants need the same dependency, also turn CONFIG_MT76_NPU into a tristate symbol that has the same dependency.

---
 drivers/net/wireless/mediatek/mt76/Kconfig        | 4 ++--
 drivers/net/wireless/mediatek/mt76/Makefile       | 6 +++++-
 drivers/net/wireless/mediatek/mt76/mt76.h         | 2 +-
 drivers/net/wireless/mediatek/mt76/mt7996/Kconfig | 2 +-
 4 files changed, 9 insertions(+), 5 deletions(-)
drivers/net/wireless/mediatek/mt76/Kconfig +2 -2
index 502303622a5382..2ca96e0527c029 100644
@@ -38,8 +38,8 @@ config MT792x_USB
 	select MT76_USB
 
 config MT76_NPU
-	bool
-	depends on MT76_CORE
+	tristate
+	depends on NET_AIROHA_NPU=y || MT76_CORE=NET_AIROHA_NPU
 
 source "drivers/net/wireless/mediatek/mt76/mt76x0/Kconfig"
 source "drivers/net/wireless/mediatek/mt76/mt76x2/Kconfig"
drivers/net/wireless/mediatek/mt76/Makefile +5 -1
index 1d42adfe803044..cacdd2b13d05bf 100644
@@ -12,7 +12,11 @@ mt76-y := \
 	mmio.o util.o trace.o dma.o mac80211.o debugfs.o eeprom.o \
 	tx.o agg-rx.o mcu.o wed.o scan.o channel.o
 
-mt76-$(CONFIG_MT76_NPU) += npu.o
+ifdef CONFIG_MT76_NPU
+# CONFIG_MT76_NPU is tristate to simplify dependency tracking,
+# but it behaves as a bool symbol here.
+mt76-y += npu.o
+endif
 mt76-$(CONFIG_PCI) += pci.o
 mt76-$(CONFIG_NL80211_TESTMODE) += testmode.o
 
drivers/net/wireless/mediatek/mt76/mt76.h +1 -1
index 527bef97e122a1..0eea08553069cb 100644
@@ -1646,7 +1646,7 @@ int mt76_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *skb,
 int mt76_testmode_set_state(struct mt76_phy *phy, enum mt76_testmode_state state);
 int mt76_testmode_alloc_skb(struct mt76_phy *phy, u32 len);
 
-#ifdef CONFIG_MT76_NPU
+#if IS_ENABLED(CONFIG_MT76_NPU)
 void mt76_npu_check_ppe(struct mt76_dev *dev, struct sk_buff *skb,
 			u32 info);
 int mt76_npu_dma_add_buf(struct mt76_phy *phy, struct mt76_queue *q,
drivers/net/wireless/mediatek/mt76/mt7996/Kconfig +1 -1
index 5503d03bf62c65..5742bce12fbb58 100644
@@ -16,6 +16,6 @@ config MT7996E
 config MT7996_NPU
 	bool "MT7996 (PCIe) NPU support"
 	depends on MT7996E
-	depends on NET_AIROHA_NPU=y || MT7996E=NET_AIROHA_NPU
+	depends on NET_AIROHA_NPU=y || MT76_CORE=NET_AIROHA_NPU
 	select MT76_NPU
 	default n
Signature · 1 line
2.53.0
author maintainer
#297 lore raw

PATCH 295/744 wifi: mt76: mt7925: fix crash in reset link replay

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Sean Wang <[email protected]>

[ Upstream commit bd8b2ec838184236c3fcbf738a926328836adf12 ]

During reset recovery, mt7925_vif_connect_iter() replays firmware state for links tracked in mvif->valid_links. After MLO link changes or MCU timeout recovery, the driver bitmap can temporarily contain a link whose mac80211 bss_conf has already gone away.

This can pass a NULL bss_conf to mt76_connac_mcu_uni_add_dev(), matching the crash where x1, the second argument, is NULL:

pc : mt76_connac_mcu_uni_add_dev+0x8c/0x1f8 [mt76_connac_lib]
lr : mt7925_vif_connect_iter+0x9c/0x168 [mt7925_common]
x2 : ffffff80a77f6018 x1 : 0000000000000000 x0 : ffffff8099402080
Call trace:
mt76_connac_mcu_uni_add_dev+0x8c/0x1f8 [mt76_connac_lib]
mt7925_vif_connect_iter+0x9c/0x168 [mt7925_common]
mt7925_mac_reset_work+0x264/0x2f8 [mt7925_common]

Skip missing bss_conf entries before replaying the link. Non-MLO AP/STA reset replay is unchanged because the helper still returns &vif->bss_conf for the legacy link.

Fixes 14061994184d ("wifi: mt76: mt7925: add link handling in mt7925_vif_connect_iter")
---
 drivers/net/wireless/mediatek/mt76/mt7925/mac.c | 3 +++
 1 file changed, 3 insertions(+)
drivers/net/wireless/mediatek/mt76/mt7925/mac.c +3 -0
index 50034d7c04f0cf..717c52ad9c1e36 100644
@@ -1275,6 +1275,9 @@ mt7925_vif_connect_iter(void *priv, u8 *mac,
 
 	for_each_set_bit(i, &valid, IEEE80211_MLD_MAX_NUM_LINKS) {
 		bss_conf = mt792x_vif_to_bss_conf(vif, i);
+		if (!bss_conf)
+			continue;
+
 		mconf = mt792x_vif_to_link(mvif, i);
 
 		mt76_connac_mcu_uni_add_dev(&dev->mphy, bss_conf, &mconf->mt76,
Signature · 1 line
2.53.0
author maintainer
#298 lore raw

PATCH 296/744 wifi: mt76: mt7996: fix possible NULL-pointer deref in mt7996_mcu_sta_bfer_eht()

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Lorenzo Bianconi <[email protected]>

[ Upstream commit 2fffc472bec490c8357defcee9c075ca74467352 ]

mt76_connac_get_eht_phy_cap routine can theoretically return NULL so check cap pointer before dereferencing it.

---
 drivers/net/wireless/mediatek/mt76/mt7996/mcu.c | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)
drivers/net/wireless/mediatek/mt76/mt7996/mcu.c +7 -3
index a6a21ddd17a2a0..9772475c5e6b28 100644
@@ -1920,14 +1920,18 @@ mt7996_mcu_sta_bfer_eht(struct ieee80211_link_sta *link_sta,
 	struct ieee80211_sta_eht_cap *pc = &link_sta->eht_cap;
 	struct ieee80211_eht_cap_elem_fixed *pe = &pc->eht_cap_elem;
 	struct ieee80211_eht_mcs_nss_supp *eht_nss = &pc->eht_mcs_nss_supp;
-	const struct ieee80211_sta_eht_cap *vc =
-		mt76_connac_get_eht_phy_cap(phy->mt76, vif);
-	const struct ieee80211_eht_cap_elem_fixed *ve = &vc->eht_cap_elem;
 	u8 nss_mcs = u8_get_bits(eht_nss->bw._80.rx_tx_mcs9_max_nss,
 				 IEEE80211_EHT_MCS_NSS_RX) - 1;
+	const struct ieee80211_eht_cap_elem_fixed *ve;
+	const struct ieee80211_sta_eht_cap *vc;
 	u8 snd_dim, sts;
 
+	vc = mt76_connac_get_eht_phy_cap(phy->mt76, vif);
+	if (!vc)
+		return;
+
 	bf->tx_mode = MT_PHY_TYPE_EHT_MU;
+	ve = &vc->eht_cap_elem;
 
 	mt7996_mcu_sta_sounding_rate(bf, phy);
 
Signature · 1 line
2.53.0
author maintainer
#299 lore raw

PATCH 297/744 drm/xe/vf: Add drm_dev guards when detaching CCS read/write buffers

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Satyanarayana K V P <[email protected]>

[ Upstream commit 4c92afb4c143526d340545ca581e88e6952ea511 ]

CCS read/write buffers are freed during BO destruction. In some cases, BOs may be destroyed after the device is unbound but while the DRM structure remains valid, leading to NULL pointer dereferences when accessing device resources.

BUG: kernel NULL pointer dereference, address: 0000000000000000
PGD 0 P4D 0
Oops: Oops: 0000 [#1] SMP NOPTI
CPU: 0 UID: 0 PID: 9376 Comm: xe_pat Not tainted 7.2.0-rc2+ #1 PREEMPT(lazy)
RIP: 0010:xe_sriov_vf_ccs_rw_update_bb_addr+0x4d/0xa0 [xe]
RSP: 0018:ffffcf304110b9c8 EFLAGS: 00010246
RAX: ffff8a85c38a0a00 RBX: 00000000810ef000 RCX: 0000000000000000
RDX: 0000000000000000 RSI: 0000000000000000 RDI: ffff8a85c39c1888
RBP: ffffcf304110b9e8 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000000 R12: ffff8a85c39c1888
R13: 0000000000000000 R14: ffff8a85c39b4f28 R15: ffff8a85c3885000
FS:  0000000000000000(0000) GS:ffff8a878b809000(0000) knlGS:0000000000000000
CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 0000000000000000 CR3: 000000010314a002 CR4: 0000000000772ef0
PKRU: 55555554
Call Trace:
 <TASK>
 xe_migrate_ccs_rw_copy_clear+0x98/0x120 [xe]
 xe_sriov_vf_ccs_detach_bo+0x2c/0x60 [xe]
 xe_ttm_bo_delete_mem_notify+0xc8/0xe0 [xe]
 ttm_bo_cleanup_memtype_use+0x26/0x80 [ttm]
 ttm_bo_release+0x29e/0x2d0 [ttm]
 ttm_bo_fini+0x39/0x70 [ttm]
 xe_gem_object_free+0x1f/0x30 [xe]
 drm_gem_object_free+0x1d/0x40
 ttm_bo_vm_close+0x5f/0x90 [ttm]
 remove_vma+0x2c/0x70
 tear_down_vmas+0x63/0xf0
 exit_mmap+0x20d/0x3f0
 __mmput+0x45/0x170
 mmput+0x31/0x40
 do_exit+0x2ba/0xac0
 do_group_exit+0x2d/0xb0
 __x64_sys_exit_group+0x18/0x20
 x64_sys_call+0x14a0/0x2390
 do_syscall_64+0xdd/0x640
 ? count_memcg_events+0xea/0x240
 ? handle_mm_fault+0x1ec/0x2f0
Fixes 864690cf4dd6 ("drm/xe/vf: Attach and detach CCS copy commands with BO")
Cc Matthew Brost <[email protected]>
Cc Michal Wajdeczko <[email protected]>
(cherry picked from commit 1ae415a6eefe5004954a1d352b1718faca8844ef)
---
 drivers/gpu/drm/xe/xe_migrate.c      | 16 ++++++++++------
 drivers/gpu/drm/xe/xe_migrate.h      |  3 ++-
 drivers/gpu/drm/xe/xe_sriov_vf_ccs.c | 14 ++++++++++++--
 3 files changed, 24 insertions(+), 9 deletions(-)
drivers/gpu/drm/xe/xe_migrate.c +10 -6
index f4160e844b4c33..5dd03d4cbf5bb2 100644
@@ -1292,6 +1292,7 @@ int xe_migrate_ccs_rw_copy(struct xe_tile *tile, struct xe_exec_queue *q,
  * content.
  * @src_bo: The buffer object @src is currently bound to.
  * @read_write : Creates BB commands for CCS read/write.
+ * @bound: Device is bound
  *
  * Directly clearing the BB lacks atomicity and can lead to undefined
  * behavior if the vCPU is halted mid-operation during the clearing
@@ -1304,7 +1305,8 @@ int xe_migrate_ccs_rw_copy(struct xe_tile *tile, struct xe_exec_queue *q,
  * Returns: None.
  */
 void xe_migrate_ccs_rw_copy_clear(struct xe_bo *src_bo,
-				  enum xe_sriov_vf_ccs_rw_ctxs read_write)
+				  enum xe_sriov_vf_ccs_rw_ctxs read_write,
+				  bool bound)
 {
 	struct xe_mem_pool_node *bb = src_bo->bb_ccs[read_write];
 	struct xe_device *xe = xe_bo_device(src_bo);
@@ -1318,13 +1320,15 @@ void xe_migrate_ccs_rw_copy_clear(struct xe_bo *src_bo,
 	bb_pool = ctx->mem.ccs_bb_pool;
 
 	scoped_guard(mutex, xe_mem_pool_bo_swap_guard(bb_pool)) {
-		xe_mem_pool_swap_shadow_locked(bb_pool);
+		if (bound) {
+			xe_mem_pool_swap_shadow_locked(bb_pool);
 
-		cs = xe_mem_pool_node_cpu_addr(bb);
-		memset(cs, MI_NOOP, bb->sa_node.size);
-		xe_sriov_vf_ccs_rw_update_bb_addr(ctx);
+			cs = xe_mem_pool_node_cpu_addr(bb);
+			memset(cs, MI_NOOP, bb->sa_node.size);
+			xe_sriov_vf_ccs_rw_update_bb_addr(ctx);
 
-		xe_mem_pool_sync_shadow_locked(bb);
+			xe_mem_pool_sync_shadow_locked(bb);
+		}
 		xe_mem_pool_free_node(bb);
 		src_bo->bb_ccs[read_write] = NULL;
 	}
drivers/gpu/drm/xe/xe_migrate.h +2 -1
index 7761c02b4d86d6..0bcc122c1c3434 100644
@@ -142,7 +142,8 @@ int xe_migrate_ccs_rw_copy(struct xe_tile *tile, struct xe_exec_queue *q,
 			   enum xe_sriov_vf_ccs_rw_ctxs read_write);
 
 void xe_migrate_ccs_rw_copy_clear(struct xe_bo *src_bo,
-				  enum xe_sriov_vf_ccs_rw_ctxs read_write);
+				  enum xe_sriov_vf_ccs_rw_ctxs read_write,
+				  bool bound);
 
 struct xe_lrc *xe_migrate_lrc(struct xe_migrate *migrate);
 struct xe_exec_queue *xe_migrate_exec_queue(struct xe_migrate *migrate);
drivers/gpu/drm/xe/xe_sriov_vf_ccs.c +12 -2
index 6787564629c65f..a8c831fbee3b5a 100644
@@ -3,6 +3,8 @@
  * Copyright © 2025 Intel Corporation
  */
 
+#include <drm/drm_drv.h>
+
 #include "instructions/xe_mi_commands.h"
 #include "instructions/xe_gpu_commands.h"
 #include "xe_bb.h"
@@ -446,7 +448,7 @@ int xe_sriov_vf_ccs_attach_bo(struct xe_bo *bo, struct ttm_resource *new_mem)
 	 */
 	for_each_ccs_rw_ctx(ctx_id) {
 		if (bo->bb_ccs[ctx_id])
-			xe_migrate_ccs_rw_copy_clear(bo, ctx_id);
+			xe_migrate_ccs_rw_copy_clear(bo, ctx_id, true);
 	}
 	return err;
 }
@@ -466,19 +468,27 @@ int xe_sriov_vf_ccs_detach_bo(struct xe_bo *bo)
 	struct xe_device *xe = xe_bo_device(bo);
 	enum xe_sriov_vf_ccs_rw_ctxs ctx_id;
 	struct xe_mem_pool_node *bb;
+	bool bound;
+	int idx;
 
 	xe_assert(xe, IS_VF_CCS_READY(xe));
 
 	if (!xe_bo_has_valid_ccs_bb(bo))
 		return 0;
 
+	bound = drm_dev_enter(&xe->drm, &idx);
+
 	for_each_ccs_rw_ctx(ctx_id) {
 		bb = bo->bb_ccs[ctx_id];
 		if (!bb)
 			continue;
 
-		xe_migrate_ccs_rw_copy_clear(bo, ctx_id);
+		xe_migrate_ccs_rw_copy_clear(bo, ctx_id, bound);
 	}
+
+	if (bound)
+		drm_dev_exit(idx);
+
 	return 0;
 }
 
Signature · 1 line
2.53.0
author maintainer
#300 lore raw

PATCH 298/744 wifi: brcmfmac: fix 802.1X-SHA256 call trace warning

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Shelley Yang <[email protected]>

[ Upstream commit 7cb34f6c4fe8a68af621d870abe63bfca2275dd6 ]

Based on wpa_auth as 1x_256 mode, need to set up "use_fwsup" with BRCMF_PROFILE_FWSUP_1X.
Or it will happen trace warning when call brcmf_cfg80211_set_pmk().

[ 4481.831101] ------------[ cut here ]------------
[ 4481.831102] WARNING: CPU: 1 PID: 2997 at
drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c:7242 brcmf_cfg80211_set_pmk+0x77/0xd0 [brcmfmac]
[...]
[ 4481.831202] Call Trace:
[ 4481.831204]  <TASK>
[ 4481.831205]  nl80211_set_pmk+0x183/0x250 [cfg80211]
[ 4481.831233]  genl_family_rcv_msg_doit+0xea/0x150
[ 4481.831237]  genl_rcv_msg+0x104/0x240
[ 4481.831239]  ? cfg80211_probe_status+0x2c0/0x2c0 [cfg80211]
[ 4481.831257]  ? genl_family_rcv_msg_doit+0x150/0x150
[ 4481.831259]  netlink_rcv_skb+0x4e/0x100
[ 4481.831261]  genl_rcv+0x24/0x40
[ 4481.831262]  netlink_unicast+0x236/0x380
[ 4481.831264]  netlink_sendmsg+0x250/0x4b0
[ 4481.831266]  sock_sendmsg+0x5c/0x70
[ 4481.831269]  ____sys_sendmsg+0x236/0x2b0
[ 4481.831271]  ? copy_msghdr_from_user+0x6d/0xa0
[ 4481.831272]  ___sys_sendmsg+0x86/0xd0
[ 4481.831274]  ? avc_has_perm+0x8c/0x1a0
[ 4481.831276]  ? preempt_count_add+0x6a/0xa0
[ 4481.831279]  ? sock_has_perm+0x82/0xa0
[ 4481.831280]  __sys_sendmsg+0x57/0xa0
[ 4481.831282]  do_syscall_64+0x38/0x90
[ 4481.831284]  entry_SYSCALL_64_after_hwframe+0x63/0xcd
[ 4481.831286] RIP: 0033:0x7fd270d369b4
Fixes 2526ff21aa77 ("brcmfmac: support 4-way handshake offloading for 802.1X")
---
 drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +1 -1
index 0b55d445895f20..89f61710a21045 100644
@@ -2146,7 +2146,7 @@ brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme)
 				 sme->crypto.akm_suites[0]);
 			return -EINVAL;
 		}
-	} else if (val & (WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED)) {
+	} else if (val & (WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED | WPA2_AUTH_1X_SHA256)) {
 		switch (sme->crypto.akm_suites[0]) {
 		case WLAN_AKM_SUITE_8021X:
 			val = WPA2_AUTH_UNSPECIFIED;
Signature · 1 line
2.53.0
author maintainer
#301 lore raw

PATCH 299/744 ovl: fix trusted xattr escape prefix matching

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Yichong Chen <[email protected]>

[ Upstream commit a8e72879cd0d8422c0b47d6d3c1802274fe73b98 ]

In the trusted.* xattr namespace, ovl_is_escaped_xattr() compares one byte less than the escaped overlay xattr prefix length. This makes it match "trusted.overlay.overlay" without requiring the trailing dot.

As a result, an xattr such as "trusted.overlay.overlayfoo" is incorrectly treated as an escaped overlay xattr. This can be reproduced by setting "trusted.overlay.overlayfoo" on a lower file and listing xattrs through an overlay mount. listxattr() then exposes it as "trusted.overlay.oo", and a following getxattr() on that listed name fails with ENODATA.

Compare the full escaped prefix, including the trailing dot, so similarly-prefixed private xattrs are not misclassified.

---
 fs/overlayfs/xattrs.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
fs/overlayfs/xattrs.c +1 -1
index aa95855c70237b..859e80ae6f4057 100644
@@ -13,7 +13,7 @@ static bool ovl_is_escaped_xattr(struct super_block *sb, const char *name)
 			       OVL_XATTR_ESCAPE_USER_PREFIX_LEN) == 0;
 	else
 		return strncmp(name, OVL_XATTR_ESCAPE_TRUSTED_PREFIX,
-			       OVL_XATTR_ESCAPE_TRUSTED_PREFIX_LEN - 1) == 0;
+			       OVL_XATTR_ESCAPE_TRUSTED_PREFIX_LEN) == 0;
 }
 
 static bool ovl_is_own_xattr(struct super_block *sb, const char *name)
Signature · 1 line
2.53.0
author maintainer
#302 lore raw

PATCH 300/744 drm/panel: ilitek-ili9882t: fix unmet dependency for DRM_PANEL_ILITEK_ILI9882T

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Julian Braha <[email protected]>

[ Upstream commit fc03f930bd9fd7c09617e41b1743e50ba659707c ]

Currently, DRM_PANEL_ILITEK_ILI9882T selects DRM_DISPLAY_DSC_HELPER without ensuring DRM_DISPLAY_HELPER is also enabled, causing an unmet dependency and build failure.

Other similar options select DRM_DISPLAY_HELPER, let's do the same here.

This unmet dependency bug was found by kconfirm, a static analysis tool for Kconfig.

Fixes 68e28facbc8a ("drm/panel: ilitek-ili9882t: Select DRM_DISPLAY_DSC_HELPER")
---
 drivers/gpu/drm/panel/Kconfig | 1 +
 1 file changed, 1 insertion(+)
drivers/gpu/drm/panel/Kconfig +1 -0
index b2153e04a59af7..abc954dff88bc4 100644
@@ -308,6 +308,7 @@ config DRM_PANEL_ILITEK_ILI9882T
 	depends on OF
 	depends on DRM_MIPI_DSI
 	depends on BACKLIGHT_CLASS_DEVICE
+	select DRM_DISPLAY_HELPER
 	select DRM_DISPLAY_DSC_HELPER
 	help
 	  Say Y if you want to enable support for panels based on the
Signature · 1 line
2.53.0
author maintainer
#303 lore raw

PATCH 301/744 drm/panel: s6e3ha8: fix unmet dependency on DRM_DISPLAY_HELPER

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Julian Braha <[email protected]>

[ Upstream commit 3667bc164849fee4f1b18b182bdfe643f758ca17 ]

Currently, DRM_PANEL_SAMSUNG_S6E3HA8 selects DRM_DISPLAY_DSC_HELPER without ensuring its dependency, DRM_DISPLAY_HELPER, is enabled, causing an unmet dependency.

Let's select DRM_DISPLAY_HELPER as other similar options do.

This unmet dependency bug was found by kconfirm, a static analysis tool for Kconfig.

Fixes fd3b2c5f40a1 ("drm/panel: s6e3ha8: select CONFIG_DRM_DISPLAY_DSC_HELPER")
---
 drivers/gpu/drm/panel/Kconfig | 1 +
 1 file changed, 1 insertion(+)
drivers/gpu/drm/panel/Kconfig +1 -0
index abc954dff88bc4..db2bd4d02377bb 100644
@@ -915,6 +915,7 @@ config DRM_PANEL_SAMSUNG_S6E3HA8
 	depends on OF
 	depends on DRM_MIPI_DSI
 	depends on BACKLIGHT_CLASS_DEVICE
+	select DRM_DISPLAY_HELPER
 	select DRM_DISPLAY_DSC_HELPER
 	help
 	  Say Y or M here if you want to enable support for the
Signature · 1 line
2.53.0
author maintainer
#304 lore raw

PATCH 302/744 drm/panel: fix unmet dependency bug for DRM_PANEL_HIMAX_HX83121A

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Julian Braha <[email protected]>

[ Upstream commit aaa5be0258db1709e254b4150af5c2fa5518c548 ]

Currently, DRM_PANEL_HIMAX_HX83121A selects DRM_DISPLAY_DSC_HELPER without also ensuring DRM_DISPLAY_HELPER is enabled, causing an unmet dependency:

WARNING: unmet direct dependencies detected for DRM_DISPLAY_DSC_HELPER
Depends on [n]: HAS_IOMEM [=y] && DRM [=m] && DRM_DISPLAY_HELPER [=n]
Selected by [m]:
- DRM_PANEL_HIMAX_HX83121A [=m] && HAS_IOMEM [=y] && DRM [=m] && DRM_PANEL [=y] && OF [=y] && DRM_MIPI_DSI [=y] && BACKLIGHT_CLASS_DEVICE [=m]
- DRM_PANEL_ILITEK_ILI9882T [=m] && HAS_IOMEM [=y] && DRM [=m] && DRM_PANEL [=y] && OF [=y] && DRM_MIPI_DSI [=y] && BACKLIGHT_CLASS_DEVICE [=m]

Many other DRM_PANEL_* options select DRM_DISPLAY_HELPER when selecting DRM_DISPLAY_DSC_HELPER, let's do the same here.

This unmet dependency bug was found by kconfirm, a static analysis tool for Kconfig.

Fixes defab7b01e08 ("drm/panel: hx83121a: select DRM_DISPLAY_DSC_HELPER")
---
 drivers/gpu/drm/panel/Kconfig | 1 +
 1 file changed, 1 insertion(+)
drivers/gpu/drm/panel/Kconfig +1 -0
index db2bd4d02377bb..adab94b3ebdfd7 100644
@@ -208,6 +208,7 @@ config DRM_PANEL_HIMAX_HX83121A
 	depends on OF
 	depends on DRM_MIPI_DSI
 	depends on BACKLIGHT_CLASS_DEVICE
+	select DRM_DISPLAY_HELPER
 	select DRM_DISPLAY_DSC_HELPER
 	select DRM_KMS_HELPER
 	help
Signature · 1 line
2.53.0
author maintainer
#305 lore raw

PATCH 303/744 drm/vc4: hvs/v3d: Fix null dereference in unbind

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Gregor Herburger <[email protected]>

[ Upstream commit 7dc3680b7ffe01add3e9299fde8471d2dd53a8ae ]

The hvs and v3d drivers use dev_get_drvdata(master) in their unbind functions. Since the vc4-drm gets removed before its dependent drivers (vc4_hvs/vc4_v3d) the vc4_hvs_unbind/vc4_v3d_unbind functions try to get drvdata of its master and fails with a null dereference error.

Use the data pointer passed to the unbind functions directly instead of dev_get_drvdata(master). This avoids using potentially freed memory.

Fixes d3f5168a0810 ("drm/vc4: Bind and initialize the V3D engine.")
Fixes c8b75bca92cb ("drm/vc4: Add KMS support for Raspberry Pi.")
---
 drivers/gpu/drm/vc4/vc4_hvs.c | 2 +-
 drivers/gpu/drm/vc4/vc4_v3d.c | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)
drivers/gpu/drm/vc4/vc4_hvs.c +1 -1
index ee8d0738501b87..89359d1900087d 100644
@@ -1752,7 +1752,7 @@ static int vc4_hvs_bind(struct device *dev, struct device *master, void *data)
 static void vc4_hvs_unbind(struct device *dev, struct device *master,
 			   void *data)
 {
-	struct drm_device *drm = dev_get_drvdata(master);
+	struct drm_device *drm = data;
 	struct vc4_dev *vc4 = to_vc4_dev(drm);
 	struct vc4_hvs *hvs = vc4->hvs;
 	struct drm_mm_node *node, *next;
drivers/gpu/drm/vc4/vc4_v3d.c +1 -1
index d31b906cb8e787..f32410420d3e4d 100644
@@ -494,7 +494,7 @@ static int vc4_v3d_bind(struct device *dev, struct device *master, void *data)
 static void vc4_v3d_unbind(struct device *dev, struct device *master,
 			   void *data)
 {
-	struct drm_device *drm = dev_get_drvdata(master);
+	struct drm_device *drm = data;
 	struct vc4_dev *vc4 = to_vc4_dev(drm);
 
 	vc4_irq_uninstall(drm);
Signature · 1 line
2.53.0
author maintainer
#306 lore raw

PATCH 304/744 ovl: check access to copy_file_range source with src mounter creds

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Amir Goldstein <[email protected]>

[ Upstream commit a1e0eb8f55cfe09bb31a202a388babc411292656 ]

Commit 5dae222a5ff0c ("vfs: allow copy_file_range to copy across devices") allowed filesystems that implement the copy_file_range() f_op to decide if they want to access cross-sb copy from/to the same fs type.

The same commit added checks to verify same sb copy for filesystems that implement ->copy_file_range() and do not support cross-sb copy at the time, namely, to ceph, fuse and nfs.

The two remaining fs which implement ->copy_file_range(), cifs and overlayfs started to support cross-sb copy from this time.

While overlayfs does support cross-sb copy when the two underlying files are on the same base fs, the copy operation on the two real files from two different overalyfs filesystems is performed with the mounter creds of the destination overlayfs and the read permission access hook for the source file was called with the wrong creds.

This could cause either deny of access to copy which would otherwise be allowed (e.g. with splice) or allow read access to file which would otherwise be denied.

Fix the latter case by explicitly verifying read access to source file with the source overlayfs mounter creds.

The former case remains a quirk of cross-sb overlayfs copy, but userspace could fall back to regular copy so no harm done.

Fixes 5dae222a5ff0c ("vfs: allow copy_file_range to copy across devices")
---
 fs/overlayfs/file.c | 16 +++++++++++++++-
 1 file changed, 15 insertions(+), 1 deletion(-)
fs/overlayfs/file.c +15 -1
index 27cc07738f33bf..f3d97eb146e85b 100644
@@ -528,6 +528,7 @@ static loff_t ovl_copyfile(struct file *file_in, loff_t pos_in,
 			    struct file *file_out, loff_t pos_out,
 			    loff_t len, unsigned int flags, enum ovl_copyop op)
 {
+	struct inode *inode_in = file_inode(file_in);
 	struct inode *inode_out = file_inode(file_out);
 	struct file *realfile_in, *realfile_out;
 	loff_t ret;
@@ -551,7 +552,20 @@ static loff_t ovl_copyfile(struct file *file_in, loff_t pos_in,
 	if (IS_ERR(realfile_in))
 		goto out_unlock;
 
-	with_ovl_creds(file_inode(file_out)->i_sb) {
+	/*
+	 * For cross-sb copy, vfs_copy_file_range() will verify read access with
+	 * the mounter creds of the dest fs mounter, so we need to explicitly
+	 * verify read access with the source mounter creds.
+	 */
+	if (unlikely(inode_in->i_sb != inode_out->i_sb)) {
+		with_ovl_creds(inode_in->i_sb) {
+			ret = rw_verify_area(READ, realfile_in, &pos_in, len);
+			if (unlikely(ret))
+				goto out_unlock;
+		}
+	}
+
+	with_ovl_creds(inode_out->i_sb) {
 		switch (op) {
 		case OVL_COPY:
 			ret = vfs_copy_file_range(realfile_in, pos_in,
Signature · 1 line
2.53.0
author maintainer
#307 lore raw

PATCH 305/744 amt: re-read skb header pointers after every pull

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Michael Bommarito <[email protected]>

[ Upstream commit 3656a79f94c471827a08f2cacce5f94ad5e52c24 ]

Several AMT receive and transmit paths cache a pointer into the skb head (ip_hdr(), ipv6_hdr(), eth_hdr() or the AMT message header) and then call a helper that can reallocate that head before the cached pointer is used again. pskb_may_pull(), ip_mc_may_pull(), ipv6_mc_may_pull(), iptunnel_pull_header(), ip_mc_check_igmp() and ipv6_mc_check_mld() can all free the old head and move the data, so a pointer taken before the call dangles afterwards and the later access is a use-after-free of the freed head.

The affected sites are:

amt_rcv() caches ip_hdr() before amt_parse_type() pulls, then reads iph->saddr.

amt_dev_xmit() caches ip_hdr()/ipv6_hdr() before ip_mc_check_igmp()/ ipv6_mc_check_mld() and pskb_may_pull(), then reads the group address.

amt_multicast_data_handler() caches eth_hdr() before pskb_may_pull(), then writes the L2 header.

amt_membership_query_handler() caches the AMT header, the outer and inner eth_hdr() and ip_hdr() before iptunnel_pull_header() and several pulls, then reads and writes them.

amt_igmpv3_report_handler() and amt_mldv2_report_handler() cache ip_hdr()/ipv6_hdr() and the current group record and read the record count from the report header inside the record loop, across the *_mc_may_pull() calls.

amt_update_handler() caches ip_hdr() and the AMT membership-update header before pskb_may_pull(), iptunnel_pull_header(), ip_mc_check_igmp() and the report handler, then reads iph->daddr and amtmu->nonce / amtmu->response_mac.

Fix each site by either snapshotting the scalar that is used after the pull before the first pull runs, or re-deriving the header pointer from the skb after the last pull that can move the head. Values that are stable across the pull (source and group address, the response MAC and nonce, the record count, the outer source MAC) are snapshotted; pointers that are written through or read repeatedly are re-derived.

---
 drivers/net/amt.c | 79 +++++++++++++++++++++++++++++++++--------------
 1 file changed, 55 insertions(+), 24 deletions(-)
drivers/net/amt.c +55 -24
index 0f4ff41d053528..00452d4ed3357b 100644
@@ -1211,7 +1211,7 @@ static netdev_tx_t amt_dev_xmit(struct sk_buff *skb, struct net_device *dev)
 			data = true;
 		}
 		v6 = false;
-		group.ip4 = iph->daddr;
+		group.ip4 = ip_hdr(skb)->daddr;
 #if IS_ENABLED(CONFIG_IPV6)
 	} else if (iph->version == 6) {
 		ip6h = ipv6_hdr(skb);
@@ -1235,7 +1235,7 @@ static netdev_tx_t amt_dev_xmit(struct sk_buff *skb, struct net_device *dev)
 			data = true;
 		}
 		v6 = true;
-		group.ip6 = ip6h->daddr;
+		group.ip6 = ipv6_hdr(skb)->daddr;
 #endif
 	} else {
 		dev->stats.tx_errors++;
@@ -1278,12 +1278,12 @@ static netdev_tx_t amt_dev_xmit(struct sk_buff *skb, struct net_device *dev)
 			hlist_for_each_entry_rcu(gnode, &tunnel->groups[hash],
 						 node) {
 				if (!v6) {
-					if (gnode->group_addr.ip4 == iph->daddr)
+					if (gnode->group_addr.ip4 == group.ip4)
 						goto found;
 #if IS_ENABLED(CONFIG_IPV6)
 				} else {
 					if (ipv6_addr_equal(&gnode->group_addr.ip6,
-							    &ip6h->daddr))
+							    &group.ip6))
 						goto found;
 #endif
 				}
@@ -2000,14 +2000,18 @@ static void amt_igmpv3_report_handler(struct amt_dev *amt, struct sk_buff *skb,
 	struct igmpv3_report *ihrv3 = igmpv3_report_hdr(skb);
 	int len = skb_transport_offset(skb) + sizeof(*ihrv3);
 	void *zero_grec = (void *)&igmpv3_zero_grec;
-	struct iphdr *iph = ip_hdr(skb);
 	struct amt_group_node *gnode;
 	union amt_addr group, host;
 	struct igmpv3_grec *grec;
+	__be32 saddr;
 	u16 nsrcs;
+	u16 ngrec;
 	int i;
 
-	for (i = 0; i < ntohs(ihrv3->ngrec); i++) {
+	saddr = ip_hdr(skb)->saddr;
+	ngrec = ntohs(ihrv3->ngrec);
+
+	for (i = 0; i < ngrec; i++) {
 		len += sizeof(*grec);
 		if (!ip_mc_may_pull(skb, len))
 			break;
@@ -2019,10 +2023,13 @@ static void amt_igmpv3_report_handler(struct amt_dev *amt, struct sk_buff *skb,
 		if (!ip_mc_may_pull(skb, len))
 			break;
 
+		grec = (void *)(skb->data + len - sizeof(*grec) -
+				nsrcs * sizeof(__be32));
+
 		memset(&group, 0, sizeof(union amt_addr));
 		group.ip4 = grec->grec_mca;
 		memset(&host, 0, sizeof(union amt_addr));
-		host.ip4 = iph->saddr;
+		host.ip4 = saddr;
 		gnode = amt_lookup_group(tunnel, &group, &host, false);
 		if (!gnode) {
 			gnode = amt_add_group(amt, tunnel, &group, &host,
@@ -2162,14 +2169,18 @@ static void amt_mldv2_report_handler(struct amt_dev *amt, struct sk_buff *skb,
 	struct mld2_report *mld2r = (struct mld2_report *)icmp6_hdr(skb);
 	int len = skb_transport_offset(skb) + sizeof(*mld2r);
 	void *zero_grec = (void *)&mldv2_zero_grec;
-	struct ipv6hdr *ip6h = ipv6_hdr(skb);
 	struct amt_group_node *gnode;
 	union amt_addr group, host;
 	struct mld2_grec *grec;
+	struct in6_addr saddr;
 	u16 nsrcs;
+	u16 ngrec;
 	int i;
 
-	for (i = 0; i < ntohs(mld2r->mld2r_ngrec); i++) {
+	saddr = ipv6_hdr(skb)->saddr;
+	ngrec = ntohs(mld2r->mld2r_ngrec);
+
+	for (i = 0; i < ngrec; i++) {
 		len += sizeof(*grec);
 		if (!ipv6_mc_may_pull(skb, len))
 			break;
@@ -2181,10 +2192,13 @@ static void amt_mldv2_report_handler(struct amt_dev *amt, struct sk_buff *skb,
 		if (!ipv6_mc_may_pull(skb, len))
 			break;
 
+		grec = (void *)(skb->data + len - sizeof(*grec) -
+				nsrcs * sizeof(struct in6_addr));
+
 		memset(&group, 0, sizeof(union amt_addr));
 		group.ip6 = grec->grec_mca;
 		memset(&host, 0, sizeof(union amt_addr));
-		host.ip6 = ip6h->saddr;
+		host.ip6 = saddr;
 		gnode = amt_lookup_group(tunnel, &group, &host, true);
 		if (!gnode) {
 			gnode = amt_add_group(amt, tunnel, &group, &host,
@@ -2305,7 +2319,6 @@ static bool amt_multicast_data_handler(struct amt_dev *amt, struct sk_buff *skb)
 	skb_push(skb, sizeof(*eth));
 	skb_reset_mac_header(skb);
 	skb_pull(skb, sizeof(*eth));
-	eth = eth_hdr(skb);
 
 	if (!pskb_may_pull(skb, sizeof(*iph)))
 		return true;
@@ -2315,6 +2328,7 @@ static bool amt_multicast_data_handler(struct amt_dev *amt, struct sk_buff *skb)
 		if (!ipv4_is_multicast(iph->daddr))
 			return true;
 		skb->protocol = htons(ETH_P_IP);
+		eth = eth_hdr(skb);
 		eth->h_proto = htons(ETH_P_IP);
 		ip_eth_mc_map(iph->daddr, eth->h_dest);
 #if IS_ENABLED(CONFIG_IPV6)
@@ -2328,6 +2342,7 @@ static bool amt_multicast_data_handler(struct amt_dev *amt, struct sk_buff *skb)
 		if (!ipv6_addr_is_multicast(&ip6h->daddr))
 			return true;
 		skb->protocol = htons(ETH_P_IPV6);
+		eth = eth_hdr(skb);
 		eth->h_proto = htons(ETH_P_IPV6);
 		ipv6_eth_mc_map(&ip6h->daddr, eth->h_dest);
 #endif
@@ -2351,10 +2366,12 @@ static bool amt_membership_query_handler(struct amt_dev *amt,
 					 struct sk_buff *skb)
 {
 	struct amt_header_membership_query *amtmq;
-	struct igmpv3_query *ihv3;
 	struct ethhdr *eth, *oeth;
+	struct igmpv3_query *ihv3;
+	u8 h_source[ETH_ALEN];
 	struct iphdr *iph;
 	int hdr_size, len;
+	u64 response_mac;
 
 	hdr_size = sizeof(*amtmq) + sizeof(struct udphdr);
 	if (!pskb_may_pull(skb, hdr_size))
@@ -2367,6 +2384,8 @@ static bool amt_membership_query_handler(struct amt_dev *amt,
 	if (amtmq->nonce != amt->nonce)
 		return true;
 
+	response_mac = amtmq->response_mac;
+
 	hdr_size -= sizeof(*eth);
 	if (iptunnel_pull_header(skb, hdr_size, htons(ETH_P_TEB), false))
 		return true;
@@ -2376,6 +2395,7 @@ static bool amt_membership_query_handler(struct amt_dev *amt,
 	skb_pull(skb, sizeof(*eth));
 	skb_reset_network_header(skb);
 	eth = eth_hdr(skb);
+	ether_addr_copy(h_source, oeth->h_source);
 	if (!pskb_may_pull(skb, sizeof(*iph)))
 		return true;
 
@@ -2388,6 +2408,7 @@ static bool amt_membership_query_handler(struct amt_dev *amt,
 				   sizeof(*ihv3)))
 			return true;
 
+		iph = ip_hdr(skb);
 		if (!ipv4_is_multicast(iph->daddr))
 			return true;
 
@@ -2395,10 +2416,11 @@ static bool amt_membership_query_handler(struct amt_dev *amt,
 		skb_reset_transport_header(skb);
 		skb_push(skb, sizeof(*iph) + AMT_IPHDR_OPTS);
 		WRITE_ONCE(amt->ready4, true);
-		amt->mac = amtmq->response_mac;
+		amt->mac = response_mac;
 		amt->req_cnt = 0;
 		amt->qi = ihv3->qqic;
 		skb->protocol = htons(ETH_P_IP);
+		eth = eth_hdr(skb);
 		eth->h_proto = htons(ETH_P_IP);
 		ip_eth_mc_map(iph->daddr, eth->h_dest);
 #if IS_ENABLED(CONFIG_IPV6)
@@ -2421,10 +2443,11 @@ static bool amt_membership_query_handler(struct amt_dev *amt,
 		skb_reset_transport_header(skb);
 		skb_push(skb, sizeof(*ip6h) + AMT_IP6HDR_OPTS);
 		WRITE_ONCE(amt->ready6, true);
-		amt->mac = amtmq->response_mac;
+		amt->mac = response_mac;
 		amt->req_cnt = 0;
 		amt->qi = mld2q->mld2q_qqic;
 		skb->protocol = htons(ETH_P_IPV6);
+		eth = eth_hdr(skb);
 		eth->h_proto = htons(ETH_P_IPV6);
 		ipv6_eth_mc_map(&ip6h->daddr, eth->h_dest);
 #endif
@@ -2432,7 +2455,7 @@ static bool amt_membership_query_handler(struct amt_dev *amt,
 		return true;
 	}
 
-	ether_addr_copy(eth->h_source, oeth->h_source);
+	ether_addr_copy(eth->h_source, h_source);
 	skb->pkt_type = PACKET_MULTICAST;
 	skb->ip_summed = CHECKSUM_NONE;
 	len = skb->len;
@@ -2455,8 +2478,11 @@ static bool amt_update_handler(struct amt_dev *amt, struct sk_buff *skb)
 	struct ethhdr *eth;
 	struct iphdr *iph;
 	int len, hdr_size;
+	u64 response_mac;
+	__be32 saddr;
+	__be32 nonce;
 
-	iph = ip_hdr(skb);
+	saddr = ip_hdr(skb)->saddr;
 
 	hdr_size = sizeof(*amtmu) + sizeof(struct udphdr);
 	if (!pskb_may_pull(skb, hdr_size))
@@ -2466,15 +2492,18 @@ static bool amt_update_handler(struct amt_dev *amt, struct sk_buff *skb)
 	if (amtmu->reserved || amtmu->version)
 		return true;
 
+	nonce = amtmu->nonce;
+	response_mac = amtmu->response_mac;
+
 	if (iptunnel_pull_header(skb, hdr_size, skb->protocol, false))
 		return true;
 
 	skb_reset_network_header(skb);
 
 	list_for_each_entry_rcu(tunnel, &amt->tunnel_list, list) {
-		if (tunnel->ip4 == iph->saddr) {
-			if ((amtmu->nonce == tunnel->nonce &&
-			     amtmu->response_mac == tunnel->mac)) {
+		if (tunnel->ip4 == saddr) {
+			if ((nonce == tunnel->nonce &&
+			     response_mac == tunnel->mac)) {
 				mod_delayed_work(amt_wq, &tunnel->gc_wq,
 						 msecs_to_jiffies(amt_gmi(amt))
 								  * 3);
@@ -2508,6 +2537,7 @@ static bool amt_update_handler(struct amt_dev *amt, struct sk_buff *skb)
 		eth = eth_hdr(skb);
 		skb->protocol = htons(ETH_P_IP);
 		eth->h_proto = htons(ETH_P_IP);
+		iph = ip_hdr(skb);
 		ip_eth_mc_map(iph->daddr, eth->h_dest);
 #if IS_ENABLED(CONFIG_IPV6)
 	} else if (iph->version == 6) {
@@ -2527,6 +2557,7 @@ static bool amt_update_handler(struct amt_dev *amt, struct sk_buff *skb)
 		eth = eth_hdr(skb);
 		skb->protocol = htons(ETH_P_IPV6);
 		eth->h_proto = htons(ETH_P_IPV6);
+		ip6h = ipv6_hdr(skb);
 		ipv6_eth_mc_map(&ip6h->daddr, eth->h_dest);
 #endif
 	} else {
@@ -2772,7 +2803,7 @@ static void amt_gw_rcv(struct amt_dev *amt, struct sk_buff *skb)
 static int amt_rcv(struct sock *sk, struct sk_buff *skb)
 {
 	struct amt_dev *amt;
-	struct iphdr *iph;
+	__be32 saddr;
 	int type;
 	bool err;
 
@@ -2785,7 +2816,7 @@ static int amt_rcv(struct sock *sk, struct sk_buff *skb)
 	}
 
 	skb->dev = amt->dev;
-	iph = ip_hdr(skb);
+	saddr = ip_hdr(skb)->saddr;
 	type = amt_parse_type(skb);
 	if (type == -1) {
 		err = true;
@@ -2795,7 +2826,7 @@ static int amt_rcv(struct sock *sk, struct sk_buff *skb)
 	if (amt->mode == AMT_MODE_GATEWAY) {
 		switch (type) {
 		case AMT_MSG_ADVERTISEMENT:
-			if (iph->saddr != amt->discovery_ip) {
+			if (saddr != amt->discovery_ip) {
 				netdev_dbg(amt->dev, "Invalid Relay IP\n");
 				err = true;
 				goto drop;
@@ -2807,7 +2838,7 @@ static int amt_rcv(struct sock *sk, struct sk_buff *skb)
 			}
 			goto out;
 		case AMT_MSG_MULTICAST_DATA:
-			if (iph->saddr != amt->remote_ip) {
+			if (saddr != amt->remote_ip) {
 				netdev_dbg(amt->dev, "Invalid Relay IP\n");
 				err = true;
 				goto drop;
@@ -2818,7 +2849,7 @@ static int amt_rcv(struct sock *sk, struct sk_buff *skb)
 			else
 				goto out;
 		case AMT_MSG_MEMBERSHIP_QUERY:
-			if (iph->saddr != amt->remote_ip) {
+			if (saddr != amt->remote_ip) {
 				netdev_dbg(amt->dev, "Invalid Relay IP\n");
 				err = true;
 				goto drop;
Signature · 1 line
2.53.0
author maintainer
#308 lore raw

PATCH 306/744 amt: make the head writable before rewriting the L2 header

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Michael Bommarito <[email protected]>

[ Upstream commit 53969d704fa5b7c1751e277fac96bfc22b435eac ]

amt_multicast_data_handler(), amt_membership_query_handler() and amt_update_handler() rewrite the ethernet header of the decapsulated skb in place (eth->h_proto, eth->h_dest and, for the query, also eth->h_source) before handing it up the stack. The skb head may be shared, for example when a packet tap has cloned it on the underlay interface, so writing through it corrupts the other reader's copy.

Call skb_cow_head() before the rewrite so the head is private. It is placed before the pointers into the head are (re-)derived, so a reallocation caused by the copy is picked up by those derivations.

---
 drivers/net/amt.c | 8 ++++++++
 1 file changed, 8 insertions(+)
drivers/net/amt.c +8 -0
index 00452d4ed3357b..a12ff8126c6986 100644
@@ -2320,6 +2320,9 @@ static bool amt_multicast_data_handler(struct amt_dev *amt, struct sk_buff *skb)
 	skb_reset_mac_header(skb);
 	skb_pull(skb, sizeof(*eth));
 
+	if (skb_cow_head(skb, 0))
+		return true;
+
 	if (!pskb_may_pull(skb, sizeof(*iph)))
 		return true;
 	iph = ip_hdr(skb);
@@ -2396,6 +2399,8 @@ static bool amt_membership_query_handler(struct amt_dev *amt,
 	skb_reset_network_header(skb);
 	eth = eth_hdr(skb);
 	ether_addr_copy(h_source, oeth->h_source);
+	if (skb_cow_head(skb, 0))
+		return true;
 	if (!pskb_may_pull(skb, sizeof(*iph)))
 		return true;
 
@@ -2521,6 +2526,9 @@ static bool amt_update_handler(struct amt_dev *amt, struct sk_buff *skb)
 	if (!pskb_may_pull(skb, sizeof(*iph)))
 		return true;
 
+	if (skb_cow_head(skb, 0))
+		return true;
+
 	iph = ip_hdr(skb);
 	if (iph->version == 4) {
 		if (ip_mc_check_igmp(skb)) {
Signature · 1 line
2.53.0
author maintainer
#309 lore raw

PATCH 307/744 net: bridge: vlan: fix vlan range dumps starting with pvid

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Nikolay Aleksandrov <[email protected]>

[ Upstream commit 43171c97e4714bf601b468401b37732244639c21 ]

There is a bug in all range dumps that rely on br_vlan_can_enter_range() when the PVID is a range starting VLAN, all following VLANs that match its flags can enter the range, but when the range is filled in only the PVID VLAN is dumped and the rest of the range is discarded because br_vlan_fill_vids() checks for the PVID flag. Since the PVID VLAN can be only one, we need to break ranges around it, the best way to do that consistently for all is to alter br_vlan_can_enter_range() to take into account the PVID and return false to break the range when it's matched.

Before the fix:

$ ip l add br0 type bridge vlan_filtering 1
$ ip l add dumdum type dummy
$ ip l set dumdum master br0
$ ip l set br0 up
$ ip l set dumdum up
$ bridge vlan add dev dumdum vid 1 pvid untagged master
$ bridge vlan add dev dumdum vid 2 untagged master
$ bridge vlan show dev dumdum # use legacy dump to show all vlans
port              vlan-id
dumdum            1 PVID Egress Untagged
                  2 Egress Untagged
$ bridge -d vlan show dev dumdum # use the new dump (RTM_GETVLAN)
port              vlan-id
dumdum            1 PVID Egress Untagged
                    state forwarding mcast_router 1

VLAN 2 is missing, and if there are more matching VLANs afterwards they'd be missing too.

After the fix:
[ same setup steps ]

$ bridge vlan show dev dumdum
port              vlan-id
dumdum            1 PVID Egress Untagged
                  2 Egress Untagged
$ bridge -d vlan show dev dumdum # use the new dump (RTM_GETVLAN)
port              vlan-id
dumdum            1 PVID Egress Untagged
                    state forwarding mcast_router 1
                  2 Egress Untagged
                    state forwarding mcast_router 1
---
 net/bridge/br_netlink_tunnel.c |  3 ++-
 net/bridge/br_private.h        |  6 ++++--
 net/bridge/br_vlan.c           | 10 ++++++----
 net/bridge/br_vlan_options.c   |  3 +--
 4 files changed, 13 insertions(+), 9 deletions(-)
net/bridge/br_netlink_tunnel.c +2 -1
index 71a12da30004c7..a713668ea34f0e 100644
@@ -271,7 +271,8 @@ static void __vlan_tunnel_handle_range(const struct net_bridge_port *p,
 	if (!*v_start)
 		goto out_init;
 
-	if (v && curr_change && br_vlan_can_enter_range(v, *v_end)) {
+	if (v && curr_change &&
+	    br_vlan_can_enter_range(v, *v_end, br_get_pvid(vg))) {
 		*v_end = v;
 		return;
 	}
net/bridge/br_private.h +4 -2
index bed1b1d9b28234..6b979a5cc424c8 100644
@@ -1625,7 +1625,8 @@ void br_vlan_notify(const struct net_bridge *br,
 		    u16 vid, u16 vid_range,
 		    int cmd);
 bool br_vlan_can_enter_range(const struct net_bridge_vlan *v_curr,
-			     const struct net_bridge_vlan *range_end);
+			     const struct net_bridge_vlan *range_end,
+			     u16 pvid);
 
 void br_vlan_fill_forward_path_pvid(struct net_bridge *br,
 				    struct net_device_path_ctx *ctx,
@@ -1872,7 +1873,8 @@ static inline void br_vlan_notify(const struct net_bridge *br,
 }
 
 static inline bool br_vlan_can_enter_range(const struct net_bridge_vlan *v_curr,
-					   const struct net_bridge_vlan *range_end)
+					   const struct net_bridge_vlan *range_end,
+					   u16 pvid)
 {
 	return true;
 }
net/bridge/br_vlan.c +6 -4
index 84a180927eb73b..64a4151761d479 100644
@@ -1982,9 +1982,11 @@ void br_vlan_notify(const struct net_bridge *br,
 
 /* check if v_curr can enter a range ending in range_end */
 bool br_vlan_can_enter_range(const struct net_bridge_vlan *v_curr,
-			     const struct net_bridge_vlan *range_end)
+			     const struct net_bridge_vlan *range_end,
+			     u16 pvid)
 {
-	return v_curr->vid - range_end->vid == 1 &&
+	return v_curr->vid != pvid && range_end->vid != pvid &&
+	       v_curr->vid - range_end->vid == 1 &&
 	       range_end->flags == v_curr->flags &&
 	       br_vlan_opts_eq_range(v_curr, range_end);
 }
@@ -2066,8 +2068,8 @@ static int br_vlan_dump_dev(const struct net_device *dev,
 			idx += range_end->vid - range_start->vid + 1;
 
 			range_start = v;
-		} else if (dump_stats || v->vid == pvid ||
-			   !br_vlan_can_enter_range(v, range_end)) {
+		} else if (dump_stats ||
+			   !br_vlan_can_enter_range(v, range_end, pvid)) {
 			u16 vlan_flags = br_vlan_flags(range_start, pvid);
 
 			if (!br_vlan_fill_vids(skb, range_start->vid,
net/bridge/br_vlan_options.c +1 -2
index 5514e1fc8d1faf..831e6d3a9c3bd7 100644
@@ -330,8 +330,7 @@ int br_vlan_process_options(const struct net_bridge *br,
 				continue;
 			}
 
-			if (v->vid == pvid ||
-			    !br_vlan_can_enter_range(v, curr_end)) {
+			if (!br_vlan_can_enter_range(v, curr_end, pvid)) {
 				br_vlan_notify(br, p, curr_start->vid,
 					       curr_end->vid, RTM_NEWVLAN);
 				curr_start = v;
Signature · 1 line
2.53.0
author maintainer
#310 lore raw

PATCH 308/744 net: hsr: fix memory leak on slave unregistration by removing synced VLANs

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Eric Dumazet <[email protected]>

[ Upstream commit dcf15eaf5641812f1cfc5e96537380132a7da89d ]

When an HSR master device is brought UP, it auto-adds VLAN 0 via vlan_vid0_add(), which propagates VID 0 to its slave devices (slave A and B).

If a slave device is later unregistered while HSR is active (e.g., during netns cleanup or interface destruction), hsr_del_port() is called to detach the slave port from the HSR master. However, hsr_del_port() currently does not delete the VLAN IDs that were synced to the slave device by HSR.

As a result, the slave device retains a refcount on VID 0 (and any other synced VLANs). When the slave device is destroyed, its vlan_info / vlan_vid_info structure remains allocated, leading to a memory leak.

Fix this by calling vlan_vids_del_by_dev(port->dev, master->dev) in hsr_del_port() before unlinking slave A or slave B ports, matching the propagation logic in hsr_ndo_vlan_rx_add_vid() / hsr_ndo_vlan_rx_kill_vid() and the cleanup behavior in bonding and team drivers.

---
 net/hsr/hsr_slave.c | 2 ++
 1 file changed, 2 insertions(+)
net/hsr/hsr_slave.c +2 -0
index d9af9e65f72f07..01c73b4b50ddd8 100644
@@ -242,6 +242,8 @@ void hsr_del_port(struct hsr_port *port)
 		netdev_rx_handler_unregister(port->dev);
 		if (!port->hsr->fwd_offloaded)
 			dev_set_promiscuity(port->dev, -1);
+		if (port->type == HSR_PT_SLAVE_A || port->type == HSR_PT_SLAVE_B)
+			vlan_vids_del_by_dev(port->dev, master->dev);
 		netdev_upper_dev_unlink(port->dev, master->dev);
 		if (hsr->prot_version == PRP_V1 &&
 		    port->type == HSR_PT_SLAVE_B) {
Signature · 1 line
2.53.0
author maintainer
#311 lore raw

PATCH 309/744 smp: Avoid invalid per-CPU CSD lookup with CSD lock debug

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Chuyi Zhou <[email protected]>

[ Upstream commit c58ea9adf7342508c6ac0b7ad79ef10d589f9c6e ]

Commit b0473dcd4b1d ("smp: Improve smp_call_function_single() CSD-lock diagnostics") made smp_call_function_single() use the destination CPU's csd_data when CSD lock debugging is enabled. That lets the debug code associate a stuck CSD lock with the target CPU, but it also means the CPU argument is used in per_cpu_ptr() before generic_exec_single() has a chance to validate it.

This becomes unsafe when smp_call_function_any() cannot find an online CPU in the supplied mask. In that case the selected CPU can be nr_cpu_ids, and the !wait path calls get_single_csd_data(cpu) before generic_exec_single() returns -ENXIO. With csdlock_debug_enabled set, that indexes the per-CPU offset array with an invalid CPU number.

Use the destination CPU's csd_data only when the CPU number is within nr_cpu_ids. For invalid CPU numbers, fall back to the local CPU's csd_data and let generic_exec_single() perform the existing validation and return -ENXIO.

---
 kernel/smp.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
kernel/smp.c +2 -1
index a0bb56bd8ddadb..dc6582bb35d084 100644
@@ -380,7 +380,8 @@ static DEFINE_PER_CPU_SHARED_ALIGNED(call_single_data_t, csd_data);
 #ifdef CONFIG_CSD_LOCK_WAIT_DEBUG
 static call_single_data_t *get_single_csd_data(int cpu)
 {
-	if (static_branch_unlikely(&csdlock_debug_enabled))
+	if (static_branch_unlikely(&csdlock_debug_enabled) &&
+	    (unsigned int)cpu < nr_cpu_ids)
 		return per_cpu_ptr(&csd_data, cpu);
 	return this_cpu_ptr(&csd_data);
 }
Signature · 1 line
2.53.0
author maintainer
#312 lore raw

PATCH 310/744 smp: Make CSD lock acquisition atomic for debug mode

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Chuyi Zhou <[email protected]>

[ Upstream commit 35551efb155e3b83445a6c3f66cb498d5efc182c ]

Commit b0473dcd4b1d ("smp: Improve smp_call_function_single() CSD-lock diagnostics") changed smp_call_function_single() so that, when CSD lock debugging is enabled, async !wait calls use the destination CPU csd_data. That improves diagnostics, but it also removes the single-writer property that made the old csd_lock() safe: multiple CPUs can now prepare the same destination CPU CSD concurrently.

csd_lock() currently waits for CSD_FLAG_LOCK to clear and then sets the bit with a non-atomic read-modify-write. Two senders can both see an unlocked CSD, set the bit, overwrite the callback fields, and enqueue the same llist node. Re-adding a node that is already the queue head can make node->next point to itself, leaving the target CPU stuck walking call_single_queue. Later synchronous work, such as a TLB shootdown, can then remain queued and trigger soft-lockup warnings or panics.

Keep the single csd_lock() implementation, but when CSD lock debugging is enabled, acquire CSD_FLAG_LOCK with try_cmpxchg_acquire(). This makes the destination CPU CSD a real atomic lock in the only configuration where it can be shared by multiple remote senders, while preserving the existing non-debug fast path.

Fixes b0473dcd4b1d ("smp: Improve smp_call_function_single() CSD-lock diagnostics")
---
 kernel/smp.c | 27 +++++++++++++++++++++++----
 1 file changed, 23 insertions(+), 4 deletions(-)
kernel/smp.c +23 -4
index dc6582bb35d084..52dffc86555cd2 100644
@@ -137,10 +137,10 @@ csd_do_func(smp_call_func_t func, void *info, call_single_data_t *csd)
 	trace_csd_function_exit(func, csd);
 }
 
-#ifdef CONFIG_CSD_LOCK_WAIT_DEBUG
-
 static DEFINE_STATIC_KEY_MAYBE(CONFIG_CSD_LOCK_WAIT_DEBUG_DEFAULT, csdlock_debug_enabled);
 
+#ifdef CONFIG_CSD_LOCK_WAIT_DEBUG
+
 /*
  * Parse the csdlock_debug= kernel boot parameter.
  *
@@ -342,6 +342,10 @@ static __always_inline void csd_lock_wait(call_single_data_t *csd)
 	smp_cond_load_acquire(&csd->node.u_flags, !(VAL & CSD_FLAG_LOCK));
 }
 #else
+static __always_inline void __csd_lock_wait(call_single_data_t *csd)
+{
+}
+
 static void csd_lock_record(call_single_data_t *csd)
 {
 }
@@ -354,8 +358,23 @@ static __always_inline void csd_lock_wait(call_single_data_t *csd)
 
 static __always_inline void csd_lock(call_single_data_t *csd)
 {
-	csd_lock_wait(csd);
-	csd->node.u_flags |= CSD_FLAG_LOCK;
+	if (IS_ENABLED(CONFIG_CSD_LOCK_WAIT_DEBUG) &&
+	    static_branch_unlikely(&csdlock_debug_enabled)) {
+
+		for (;;) {
+			unsigned int flags;
+
+			__csd_lock_wait(csd);
+			flags = READ_ONCE(csd->node.u_flags);
+
+			if (!(flags & CSD_FLAG_LOCK) &&
+			    try_cmpxchg_acquire(&csd->node.u_flags, &flags, flags | CSD_FLAG_LOCK))
+				break;
+		}
+	} else {
+		csd_lock_wait(csd);
+		csd->node.u_flags |= CSD_FLAG_LOCK;
+	}
 
 	/*
 	 * prevent CPU from reordering the above assignment
Signature · 1 line
2.53.0
author maintainer
#313 lore raw

PATCH 311/744 net: dpaa: fix mode setting

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Michael Walle <[email protected]>

[ Upstream commit da2c6bcc5e30b1496ac587785dcacf6e849eb6ef ]

Before converting to the phylink interface, the init function would have set a non-reserved I/F mode in the maccfg2 register. After converting to phylink, 0 is written as mode, which is a reserved value (although it's the hardware default). Without a valid mode, a SGMII link is never established between the MAC and the PHY and thus .link_up() is never called which could set the correct mode according to the actual speed.

Fix it by setting the maximum speed of the phy_interface_t in use in .mac_config() - just like the driver did before the phylink conversion.

---
 .../net/ethernet/freescale/fman/fman_dtsec.c    | 17 ++++++++++++-----
 1 file changed, 12 insertions(+), 5 deletions(-)
drivers/net/ethernet/freescale/fman/fman_dtsec.c +12 -5
index fe35703c509e55..b8d70c0ecb6c66 100644
@@ -900,22 +900,28 @@ static void dtsec_mac_config(struct phylink_config *config, unsigned int mode,
 {
 	struct mac_device *mac_dev = fman_config_to_mac(config);
 	struct dtsec_regs __iomem *regs = mac_dev->fman_mac->regs;
-	u32 tmp;
+	u32 ecntrl, maccfg2;
+
+	maccfg2 = ioread32be(&regs->maccfg2);
+	maccfg2 &= ~(MACCFG2_NIBBLE_MODE | MACCFG2_BYTE_MODE);
 
 	switch (state->interface) {
 	case PHY_INTERFACE_MODE_RMII:
-		tmp = DTSEC_ECNTRL_RMM;
+		ecntrl = DTSEC_ECNTRL_RMM;
+		maccfg2 |= MACCFG2_NIBBLE_MODE;
 		break;
 	case PHY_INTERFACE_MODE_RGMII:
 	case PHY_INTERFACE_MODE_RGMII_ID:
 	case PHY_INTERFACE_MODE_RGMII_RXID:
 	case PHY_INTERFACE_MODE_RGMII_TXID:
-		tmp = DTSEC_ECNTRL_GMIIM | DTSEC_ECNTRL_RPM;
+		ecntrl = DTSEC_ECNTRL_GMIIM | DTSEC_ECNTRL_RPM;
+		maccfg2 |= MACCFG2_BYTE_MODE;
 		break;
 	case PHY_INTERFACE_MODE_SGMII:
 	case PHY_INTERFACE_MODE_1000BASEX:
 	case PHY_INTERFACE_MODE_2500BASEX:
-		tmp = DTSEC_ECNTRL_TBIM | DTSEC_ECNTRL_SGMIIM;
+		ecntrl = DTSEC_ECNTRL_TBIM | DTSEC_ECNTRL_SGMIIM;
+		maccfg2 |= MACCFG2_BYTE_MODE;
 		break;
 	default:
 		dev_warn(mac_dev->dev, "cannot configure dTSEC for %s\n",
@@ -923,7 +929,8 @@ static void dtsec_mac_config(struct phylink_config *config, unsigned int mode,
 		return;
 	}
 
-	iowrite32be(tmp, &regs->ecntrl);
+	iowrite32be(ecntrl, &regs->ecntrl);
+	iowrite32be(maccfg2, &regs->maccfg2);
 }
 
 static void dtsec_link_up(struct phylink_config *config, struct phy_device *phy,
Signature · 1 line
2.53.0
author maintainer
#314 lore raw

PATCH 312/744 net: stmmac: dwmac4: mask interrupts when stopping DMA in suspend

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Luis Lang <[email protected]>

[ Upstream commit 59a57128ae5231f9aa9d544fa9d3e38986f0efaa ]

Since commit 1b9707e6f1a9 ("net: stmmac: enable RPS and RBU interrupts"), suspending causes an interrupt storm from the RPS interrupt.
Fix this by adding a deinit_chan() op to stmmac_dma_ops, which masks all default dma channel interrupts. This is called from stmmac_stop_all_dma(), so interrupts don't trigger while suspending.

---
 .../net/ethernet/stmicro/stmmac/dwmac4_dma.c  | 24 +++++++++++++++++++
 drivers/net/ethernet/stmicro/stmmac/hwif.h    |  4 ++++
 .../net/ethernet/stmicro/stmmac/stmmac_main.c |  4 ++++
 3 files changed, 32 insertions(+)
drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c +24 -0
index 28728271fbc9ef..55c170b930106c 100644
@@ -106,6 +106,17 @@ static void dwmac4_dma_init_channel(struct stmmac_priv *priv,
 	       ioaddr + DMA_CHAN_INTR_ENA(dwmac4_addrs, chan));
 }
 
+static void dwmac4_dma_deinit_channel(struct stmmac_priv *priv,
+				      void __iomem *ioaddr, u32 chan)
+{
+	const struct dwmac4_addrs *dwmac4_addrs = priv->plat->dwmac4_addrs;
+	u32 value;
+
+	value = readl(ioaddr + DMA_CHAN_INTR_ENA(dwmac4_addrs, chan));
+	value &= ~DMA_CHAN_INTR_DEFAULT_MASK;
+	writel(value, ioaddr + DMA_CHAN_INTR_ENA(dwmac4_addrs, chan));
+}
+
 static void dwmac410_dma_init_channel(struct stmmac_priv *priv,
 				      void __iomem *ioaddr,
 				      struct stmmac_dma_cfg *dma_cfg, u32 chan)
@@ -125,6 +136,17 @@ static void dwmac410_dma_init_channel(struct stmmac_priv *priv,
 	       ioaddr + DMA_CHAN_INTR_ENA(dwmac4_addrs, chan));
 }
 
+static void dwmac410_dma_deinit_channel(struct stmmac_priv *priv,
+					void __iomem *ioaddr, u32 chan)
+{
+	const struct dwmac4_addrs *dwmac4_addrs = priv->plat->dwmac4_addrs;
+	u32 value;
+
+	value = readl(ioaddr + DMA_CHAN_INTR_ENA(dwmac4_addrs, chan));
+	value &= ~DMA_CHAN_INTR_DEFAULT_MASK_4_10;
+	writel(value, ioaddr + DMA_CHAN_INTR_ENA(dwmac4_addrs, chan));
+}
+
 static void dwmac4_dma_init(void __iomem *ioaddr,
 			    struct stmmac_dma_cfg *dma_cfg)
 {
@@ -547,6 +569,7 @@ const struct stmmac_dma_ops dwmac4_dma_ops = {
 	.reset = dwmac4_dma_reset,
 	.init = dwmac4_dma_init,
 	.init_chan = dwmac4_dma_init_channel,
+	.deinit_chan = dwmac4_dma_deinit_channel,
 	.init_rx_chan = dwmac4_dma_init_rx_chan,
 	.init_tx_chan = dwmac4_dma_init_tx_chan,
 	.axi = dwmac4_dma_axi,
@@ -576,6 +599,7 @@ const struct stmmac_dma_ops dwmac410_dma_ops = {
 	.reset = dwmac4_dma_reset,
 	.init = dwmac4_dma_init,
 	.init_chan = dwmac410_dma_init_channel,
+	.deinit_chan = dwmac410_dma_deinit_channel,
 	.init_rx_chan = dwmac4_dma_init_rx_chan,
 	.init_tx_chan = dwmac4_dma_init_tx_chan,
 	.axi = dwmac4_dma_axi,
drivers/net/ethernet/stmicro/stmmac/hwif.h +4 -0
index e6317b94fff7d4..04dafec021b4f9 100644
@@ -170,6 +170,8 @@ struct stmmac_dma_ops {
 	void (*init)(void __iomem *ioaddr, struct stmmac_dma_cfg *dma_cfg);
 	void (*init_chan)(struct stmmac_priv *priv, void __iomem *ioaddr,
 			  struct stmmac_dma_cfg *dma_cfg, u32 chan);
+	void (*deinit_chan)(struct stmmac_priv *priv, void __iomem *ioaddr,
+			    u32 chan);
 	void (*init_rx_chan)(struct stmmac_priv *priv, void __iomem *ioaddr,
 			     struct stmmac_dma_cfg *dma_cfg,
 			     dma_addr_t phy, u32 chan);
@@ -235,6 +237,8 @@ struct stmmac_dma_ops {
 	stmmac_do_void_callback(__priv, dma, init, __args)
 #define stmmac_init_chan(__priv, __args...) \
 	stmmac_do_void_callback(__priv, dma, init_chan, __priv, __args)
+#define stmmac_deinit_chan(__priv, __args...) \
+	stmmac_do_void_callback(__priv, dma, deinit_chan, __priv, __args)
 #define stmmac_init_rx_chan(__priv, __args...) \
 	stmmac_do_void_callback(__priv, dma, init_rx_chan, __priv, __args)
 #define stmmac_init_tx_chan(__priv, __args...) \
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +4 -0
index 3591755ea30be5..50dfdf5bd3eb02 100644
@@ -2560,6 +2560,7 @@ static void stmmac_stop_all_dma(struct stmmac_priv *priv)
 {
 	u8 rx_channels_count = priv->plat->rx_queues_to_use;
 	u8 tx_channels_count = priv->plat->tx_queues_to_use;
+	u8 dma_csr_ch = max(rx_channels_count, tx_channels_count);
 	u8 chan;
 
 	for (chan = 0; chan < rx_channels_count; chan++)
@@ -2567,6 +2568,9 @@ static void stmmac_stop_all_dma(struct stmmac_priv *priv)
 
 	for (chan = 0; chan < tx_channels_count; chan++)
 		stmmac_stop_tx_dma(priv, chan);
+
+	for (chan = 0; chan < dma_csr_ch; chan++)
+		stmmac_deinit_chan(priv, priv->ioaddr, chan);
 }
 
 /**
Signature · 1 line
2.53.0
author maintainer
#315 lore raw

PATCH 313/744 sctp: auth: verify auth requirement when auth_chunk is NULL

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Qing Luo <[email protected]>

[ Upstream commit 8e04823c120b376ef7dab14b60ebf6823aa16c14 ]

sctp_auth_chunk_verify() returns true unconditionally when chunk->auth_chunk is NULL, silently skipping authentication. This is incorrect when:

1. skb_clone() failed in the BH receive path, leaving auth_chunk NULL. In sctp_endpoint_bh_rcv() asoc is NULL for new connections, so the early sctp_auth_recv_cid() check cannot catch this.

2. No AUTH chunk precedes COOKIE-ECHO, so skb_clone() is never called and auth_chunk remains NULL.

Fix by checking sctp_auth_recv_cid() when auth_chunk is NULL:
if authentication is required, return false to drop the chunk; otherwise continue normally.

Fixes bbd0d59809f9 ("[SCTP]: Implement the receive and verification of AUTH chunk")
---
 net/sctp/sm_statefuns.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
net/sctp/sm_statefuns.c +1 -1
index 3893b44448b381..708fa07d5fffc7 100644
@@ -643,7 +643,7 @@ static bool sctp_auth_chunk_verify(struct net *net, struct sctp_chunk *chunk,
 	struct sctp_chunk auth;
 
 	if (!chunk->auth_chunk)
-		return true;
+		return !sctp_auth_recv_cid(chunk->chunk_hdr->type, asoc);
 
 	/* SCTP-AUTH:  auth_chunk pointer is only set when the cookie-echo
 	 * is supposed to be authenticated and we have to do delayed
Signature · 1 line
2.53.0
author maintainer
#316 lore raw

PATCH 314/744 vmxnet3: fix BUG_ON in vmxnet3_get_hdr_len() for Geneve packets

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Harshaka Narayana <[email protected]>

[ Upstream commit 34a71f5361fc3adb5b7138da78750b0d535a8252 ]

vmxnet3_get_hdr_len() assumes gdesc->rcd.v4/v6/tcp always describe the outer header, but for a Geneve-encapsulated packet the device can set them based on the inner header instead, signalled by the VMXNET3_RCD_HDR_INNER_SHIFT bit in the completion descriptor. Since the function never skips the outer encapsulation, this mismatch triggers:

- BUG_ON(hdr.ipv4->protocol != IPPROTO_TCP), because the outer protocol is UDP (Geneve), not TCP.
- BUG_ON(hdr.eth->h_proto != ...), when the tunnel's outer and inner IP versions differ (e.g. outer IPv6/inner IPv4 or vice versa).

Check VMXNET3_RCD_HDR_INNER_SHIFT up front and bail out, since the function cannot locate the inner header it would need to parse. Also convert the remaining BUG_ON()s in this function to return 0 defensively.

---
 drivers/net/vmxnet3/vmxnet3_drv.c | 22 ++++++++++++++++------
 1 file changed, 16 insertions(+), 6 deletions(-)
drivers/net/vmxnet3/vmxnet3_drv.c +16 -6
index 40522afc053203..f8df83f9965db5 100644
@@ -1530,7 +1530,11 @@ vmxnet3_get_hdr_len(struct vmxnet3_adapter *adapter, struct sk_buff *skb,
 		struct ipv6hdr *ipv6;
 		struct tcphdr *tcp;
 	} hdr;
-	BUG_ON(gdesc->rcd.tcp == 0);
+
+	/* v4/v6/tcp then describe the inner header, which we can't locate. */
+	if ((le32_to_cpu(gdesc->dword[0]) & (1UL << VMXNET3_RCD_HDR_INNER_SHIFT)) ||
+	    gdesc->rcd.tcp == 0)
+		return 0;
 
 	maplen = skb_headlen(skb);
 	if (unlikely(sizeof(struct iphdr) + sizeof(struct tcphdr) > maplen))
@@ -1544,15 +1548,21 @@ vmxnet3_get_hdr_len(struct vmxnet3_adapter *adapter, struct sk_buff *skb,
 
 	hdr.eth = eth_hdr(skb);
 	if (gdesc->rcd.v4) {
-		BUG_ON(hdr.eth->h_proto != htons(ETH_P_IP) &&
-		       hdr.veth->h_vlan_encapsulated_proto != htons(ETH_P_IP));
+		if (hdr.eth->h_proto != htons(ETH_P_IP) &&
+		    hdr.veth->h_vlan_encapsulated_proto != htons(ETH_P_IP))
+			return 0;
+
 		hdr.ptr += hlen;
-		BUG_ON(hdr.ipv4->protocol != IPPROTO_TCP);
+		if (hdr.ipv4->protocol != IPPROTO_TCP)
+			return 0;
+
 		hlen = hdr.ipv4->ihl << 2;
 		hdr.ptr += hdr.ipv4->ihl << 2;
 	} else if (gdesc->rcd.v6) {
-		BUG_ON(hdr.eth->h_proto != htons(ETH_P_IPV6) &&
-		       hdr.veth->h_vlan_encapsulated_proto != htons(ETH_P_IPV6));
+		if (hdr.eth->h_proto != htons(ETH_P_IPV6) &&
+		    hdr.veth->h_vlan_encapsulated_proto != htons(ETH_P_IPV6))
+			return 0;
+
 		hdr.ptr += hlen;
 		/* Use an estimated value, since we also need to handle
 		 * TSO case.
Signature · 1 line
2.53.0
author maintainer
#317 lore raw

PATCH 315/744 drm/xe/i2c: Allow per domain unique id

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Raag Jadav <[email protected]>

[ Upstream commit 5d8ed6b64220ad629aade5f174e3f690c37435f9 ]

PCI bus, device and function can be same for devices existing across different domains. Allow per domain unique identifier while registering platform device to prevent name conflict.

Fixes f0e53aadd702 ("drm/xe: Support for I2C attached MCUs")
(cherry picked from commit a79f6abc8b516b5bd906e2eca8121e3549ee163f)
---
 drivers/gpu/drm/xe/xe_i2c.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)
drivers/gpu/drm/xe/xe_i2c.c +4 -1
index 706783863d07d6..f05f23221c1b7b 100644
@@ -95,18 +95,21 @@ static int xe_i2c_register_adapter(struct xe_i2c *i2c)
 	struct platform_device *pdev;
 	struct fwnode_handle *fwnode;
 	int ret;
+	u32 id;
 
 	fwnode = fwnode_create_software_node(xe_i2c_adapter_properties, NULL);
 	if (IS_ERR(fwnode))
 		return PTR_ERR(fwnode);
 
+	id = (pci_domain_nr(pci->bus) << 16) | pci_dev_id(pci);
+
 	/*
 	 * Not using platform_device_register_full() here because we don't have
 	 * a handle to the platform_device before it returns. xe_i2c_notifier()
 	 * uses that handle, but it may be called before
 	 * platform_device_register_full() is done.
 	 */
-	pdev = platform_device_alloc(adapter_name, pci_dev_id(pci));
+	pdev = platform_device_alloc(adapter_name, id);
 	if (!pdev) {
 		ret = -ENOMEM;
 		goto err_fwnode_remove;
Signature · 1 line
2.53.0
author maintainer
#318 lore raw

PATCH 316/744 drm/xe/vm: Fix SVM leak on resv obj alloc failure in xe_vm_create()

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Shuicheng Lin <[email protected]>

[ Upstream commit d2c6800ad1802bed72a6de1416536737f114f1d6 ]

Commit 9e9787414882 ("drm/xe/userptr: replace xe_hmm with gpusvm") made xe_svm_init() unconditional in xe_vm_create() and extended it to also initialize a "simple" gpusvm state for non-fault-mode VMs. The matching xe_svm_fini() call in xe_vm_close_and_put() was updated to run unconditionally, but the error unwind path in xe_vm_create() was not.

On the drm_gpuvm_resv_object_alloc() failure path, xe_svm_init() has already succeeded but xe_svm_fini() is only called when XE_VM_FLAG_FAULT_MODE is set. For non-fault-mode VMs this leaves vm->svm.gpusvm partially initialized and leaks the resources allocated by drm_gpusvm_init().

For fault-mode VMs, xe_svm_init() additionally acquires the pagemap owner via drm_pagemap_acquire_owner() and the pagemaps via xe_svm_get_pagemaps(). Those resources are released by xe_svm_close(), not xe_svm_fini(). On the same error path, xe_svm_close() is not called either, so fault-mode VMs leak the pagemap owner and pagemaps.

Fix both leaks:

- Call xe_svm_fini() unconditionally on the err_svm_fini path, matching the unconditional xe_svm_init() call. Move the vm->size = 0 assignment out of the conditional so the xe_vm_is_closed() assert in xe_svm_fini() (and xe_svm_close()) holds for both modes.

- Call xe_svm_close() for fault-mode VMs before xe_svm_fini(), matching the ordering used in xe_vm_close_and_put().

Fixes 9e9787414882 ("drm/xe/userptr: replace xe_hmm with gpusvm")
Cc Matthew Auld <[email protected]>
Assisted-by Claude:claude-opus-4.7
(cherry picked from commit ca2a3587d577ba764e0fe628fb676244fc33ddd4)
---
 drivers/gpu/drm/xe/xe_vm.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)
drivers/gpu/drm/xe/xe_vm.c +4 -4
index ab6cc1f0a78949..aec3c2a60d5888 100644
@@ -1810,10 +1810,10 @@ struct xe_vm *xe_vm_create(struct xe_device *xe, u32 flags, struct xe_file *xef)
 	return ERR_PTR(err);
 
 err_svm_fini:
-	if (flags & XE_VM_FLAG_FAULT_MODE) {
-		vm->size = 0; /* close the vm */
-		xe_svm_fini(vm);
-	}
+	vm->size = 0; /* close the vm */
+	if (flags & XE_VM_FLAG_FAULT_MODE)
+		xe_svm_close(vm);
+	xe_svm_fini(vm);
 err_no_resv:
 	mutex_destroy(&vm->snap_mutex);
 	for_each_tile(tile, xe, id)
Signature · 1 line
2.53.0
author maintainer
#319 lore raw

PATCH 317/744 iomap: correct the range of a partial dirty clear

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Zhang Yi <[email protected]>

[ Upstream commit 88c26515313169806a412a362b32a1eca53d21bd ]

The block range calculation in ifs_clear_range_dirty() is incorrect when partially clearing a range in a folio. We cannot clear the dirty bit of the first block or the last block if the start or end offset is not blocksize-aligned. This has not yet caused any issues since we always clear a whole folio in iomap_writeback_folio().

Fix this by rounding up the first block to blocksize alignment, and calculate the last block by rounding down (using truncation). Correct the nr_blks calculation accordingly.

---
 fs/iomap/buffered-io.c | 12 ++++++++----
 1 file changed, 8 insertions(+), 4 deletions(-)
fs/iomap/buffered-io.c +8 -4
index 5fa9a2c7e30eb8..116110628994df 100644
@@ -176,13 +176,17 @@ static void ifs_clear_range_dirty(struct folio *folio,
 {
 	struct inode *inode = folio->mapping->host;
 	unsigned int blks_per_folio = i_blocks_per_folio(inode, folio);
-	unsigned int first_blk = (off >> inode->i_blkbits);
-	unsigned int last_blk = (off + len - 1) >> inode->i_blkbits;
-	unsigned int nr_blks = last_blk - first_blk + 1;
+	unsigned int first_blk = round_up(off, i_blocksize(inode)) >>
+				 inode->i_blkbits;
+	unsigned int last_blk = (off + len) >> inode->i_blkbits;
 	unsigned long flags;
 
+	if (first_blk >= last_blk)
+		return;
+
 	spin_lock_irqsave(&ifs->state_lock, flags);
-	bitmap_clear(ifs->state, first_blk + blks_per_folio, nr_blks);
+	bitmap_clear(ifs->state, first_blk + blks_per_folio,
+		     last_blk - first_blk);
 	spin_unlock_irqrestore(&ifs->state_lock, flags);
 }
 
Signature · 1 line
2.53.0
author maintainer
#320 lore raw

PATCH 318/744 iomap: fix incorrect did_zero setting in iomap_zero_iter()

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Zhang Yi <[email protected]>

[ Upstream commit 7a6fd6b21d7e1737b40de1a210acf9e6a1e4d59e ]

The did_zero output parameter was unconditionally set after the loop, which is incorrect. It should only be set when the zeroing operation actually completes, not when IOMAP_F_STALE is set or when IOMAP_F_FOLIO_BATCH is set but !folio causes the loop to break early, or when iomap_iter_advance() returns an error.

This causes did_zero to be incorrectly set when zeroing a clean unwritten extent because the loop exits early without actually zeroing any data.

Fix it by using a local variable to track whether any folio was actually zeroed, and only set did_zero after the loop if zeroing happened.

---
 fs/iomap/buffered-io.c | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)
fs/iomap/buffered-io.c +6 -2
index 116110628994df..0edb21bdb2f384 100644
@@ -1540,6 +1540,7 @@ static int iomap_zero_iter(struct iomap_iter *iter, bool *did_zero,
 		const struct iomap_write_ops *write_ops)
 {
 	u64 bytes = iomap_length(iter);
+	bool zeroed = false;
 	int status;
 
 	do {
@@ -1558,6 +1559,8 @@ static int iomap_zero_iter(struct iomap_iter *iter, bool *did_zero,
 		/* a NULL folio means we're done with a folio batch */
 		if (!folio) {
 			status = iomap_iter_advance_full(iter);
+			if (status)
+				return status;
 			break;
 		}
 
@@ -1568,6 +1571,7 @@ static int iomap_zero_iter(struct iomap_iter *iter, bool *did_zero,
 				bytes);
 
 		folio_zero_range(folio, offset, bytes);
+		zeroed = true;
 		folio_mark_accessed(folio);
 
 		ret = iomap_write_end(iter, bytes, bytes, folio);
@@ -1577,10 +1581,10 @@ static int iomap_zero_iter(struct iomap_iter *iter, bool *did_zero,
 
 		status = iomap_iter_advance(iter, bytes);
 		if (status)
-			break;
+			return status;
 	} while ((bytes = iomap_length(iter)) > 0);
 
-	if (did_zero)
+	if (did_zero && zeroed)
 		*did_zero = true;
 	return status;
 }
Signature · 1 line
2.53.0
author maintainer
#321 lore raw

PATCH 319/744 tipc: fix u16 MTU truncation in media and bearer MTU validation

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Cen Zhang (Microsoft) <[email protected]>

[ Upstream commit 9f29cd8a8e7901a2617c8064ce9f50fc67b97cb8 ]

Both TIPC_NL_MEDIA_SET and TIPC_NL_BEARER_SET accept user-supplied MTU values but only enforce a minimum bound, not a maximum. When a user sets the MTU to a value exceeding U16_MAX (65535), it passes validation but is silently truncated when assigned to u16 fields l->mtu and l->advertised_mtu in tipc_link_create(). Values like 65536 (0x10000) truncate to 0, causing a division by zero in tipc_link_set_queue_limits() which computes TIPC_MAX_PUBL / (l->mtu / ITEM_SIZE). Other overflowing values (e.g. 65537-131071) produce small incorrect MTU values, resulting in link malfunction behaviors.

Crash stack (triggered as unprivileged user via user namespace):

  tipc_link_set_queue_limits  net/tipc/link.c:2531
  tipc_link_create            net/tipc/link.c:520
  tipc_node_check_dest        net/tipc/node.c:1279
  tipc_disc_rcv               net/tipc/discover.c:252
  tipc_rcv                    net/tipc/node.c:2129
  tipc_udp_recv               net/tipc/udp_media.c:392

Two independent paths lack the upper bound check:
1. tipc_udp_mtu_bad() -- called from __tipc_nl_media_set() (MEDIA_SET)
2. inline check in __tipc_nl_bearer_set() at bearer.c:1160 (BEARER_SET)

Fix both by rejecting MTU values above U16_MAX.

---
 net/tipc/netlink.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)
net/tipc/netlink.c +5 -1
index 8336a9664703fe..1307dd1a961341 100644
@@ -113,12 +113,16 @@ const struct nla_policy tipc_nl_node_policy[TIPC_NLA_NODE_MAX + 1] = {
 };
 
 /* Properties valid for media, bearer and link */
+static const struct netlink_range_validation tipc_nl_mtu_range = {
+	.max = U16_MAX,
+};
+
 const struct nla_policy tipc_nl_prop_policy[TIPC_NLA_PROP_MAX + 1] = {
 	[TIPC_NLA_PROP_UNSPEC]		= { .type = NLA_UNSPEC },
 	[TIPC_NLA_PROP_PRIO]		= { .type = NLA_U32 },
 	[TIPC_NLA_PROP_TOL]		= { .type = NLA_U32 },
 	[TIPC_NLA_PROP_WIN]		= { .type = NLA_U32 },
-	[TIPC_NLA_PROP_MTU]		= { .type = NLA_U32 },
+	[TIPC_NLA_PROP_MTU]		= NLA_POLICY_FULL_RANGE(NLA_U32, &tipc_nl_mtu_range),
 	[TIPC_NLA_PROP_BROADCAST]	= { .type = NLA_U32 },
 	[TIPC_NLA_PROP_BROADCAST_RATIO]	= { .type = NLA_U32 }
 };
Signature · 1 line
2.53.0
author maintainer
#322 lore raw

PATCH 320/744 drm/tests: shmem: Set DMA mask to 64-bit in drm_gem_shmem

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: José Expósito <[email protected]>

[ Upstream commit b04a248cfa6cfa1e7dc9ce91cb1eb88b1a70dd69 ]

drm_gem_shmem_test_purge [1] and drm_gem_shmem_test_get_pages_sgt [2] intermittently fail on ppc64le and s390x CI systems with a DMA address overflow:

DMA addr 0x0000000100307000+4096 overflow (mask ffffffff, bus limit 0)

  WARNING: kernel/dma/direct.h:114 dma_direct_map_sg+0x778/0x920

drm_gem_shmem_test_purge: ASSERTION FAILED at
drivers/gpu/drm/tests/drm_gem_shmem_test.c:330 Expected sgt is not error, but is: -5

The call chain leading to the failure is:

drm_gem_shmem_test_purge() / drm_gem_shmem_test_get_pages_sgt()

    drm_gem_shmem_get_pages_sgt()
      drm_gem_shmem_get_pages_sgt_locked() [drm_gem_shmem_helper.c]
        dma_map_sgtable()                  [mapping.c]
          __dma_map_sg_attrs()
            dma_direct_map_sg()            [direct.c]
              dma_direct_map_phys()        [kernel/dma/direct.h]
                dma_capable()              Checks addr against DMA mask
                  -> FAILS: addr > 0xFFFFFFFF

The root cause is that KUnit devices are initialized with a 32-bit DMA mask (DMA_BIT_MASK(32)) in lib/kunit/device.c. On ppc64le and s390x systems with physical memory above 4GB, page allocations can land at addresses that exceed this mask. When drm_gem_shmem_get_pages_sgt() attempts to DMA-map these pages via dma_map_sgtable(), the DMA layer rejects the mapping because the physical address overflows the 32-bit mask.

The failure is intermittent because pages may or may not be allocated above 4GB on any given run depend on memory pressure.

Fix by setting a 64-bit DMA mask on the device before calling drm_gem_shmem_get_pages_sgt() for all tests, following the same pattern already used in drm_gem_shmem_test_obj_create_private().

[1] https://s3.amazonaws.com/arr-cki-prod-trusted-artifacts/trusted-artifacts/2643976103/test_s390x/15128551935/artifacts/jobwatch/logs/recipes/21561049/tasks/220716793/results/1014626315/logs/dmesg.log
[2] https://s3.amazonaws.com/arr-cki-prod-trusted-artifacts/trusted-artifacts/2643976103/test_ppc64le/15128551933/artifacts/jobwatch/logs/recipes/21561041/tasks/220716705/results/1014628163/logs/dmesg.log

Fixes 93032ae634d4 ("drm/test: add a test suite for GEM objects backed by shmem")
Assisted-by Claude:claude-4.6-opus
---
 drivers/gpu/drm/tests/drm_gem_shmem_test.c | 16 ++++++++++------
 1 file changed, 10 insertions(+), 6 deletions(-)
drivers/gpu/drm/tests/drm_gem_shmem_test.c +10 -6
index 44a1901092497e..5e69ff1d1ad673 100644
@@ -95,13 +95,9 @@ static void drm_gem_shmem_test_obj_create_private(struct kunit *test)
 	sg_init_one(sgt->sgl, buf, TEST_SIZE);
 
 	/*
-	 * Set the DMA mask to 64-bits and map the sgtables
-	 * otherwise drm_gem_shmem_free will cause a warning
-	 * on debug kernels.
+	 * Map the sgtables otherwise drm_gem_shmem_free will cause a warning on
+	 * debug kernels.
 	 */
-	ret = dma_set_mask(drm_dev->dev, DMA_BIT_MASK(64));
-	KUNIT_ASSERT_EQ(test, ret, 0);
-
 	ret = dma_map_sgtable(drm_dev->dev, sgt, DMA_BIDIRECTIONAL, 0);
 	KUNIT_ASSERT_EQ(test, ret, 0);
 
@@ -352,11 +348,19 @@ static int drm_gem_shmem_test_init(struct kunit *test)
 {
 	struct device *dev;
 	struct drm_device *drm_dev;
+	int ret;
 
 	/* Allocate a parent device */
 	dev = drm_kunit_helper_alloc_device(test);
 	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
 
+	/*
+	 * Set the DMA mask to 64-bits to avoid intermittent failures calling
+	 * drm_gem_shmem_get_pages_sgt().
+	 */
+	ret = dma_set_mask(dev, DMA_BIT_MASK(64));
+	KUNIT_ASSERT_EQ(test, ret, 0);
+
 	/*
 	 * The DRM core will automatically initialize the GEM core and create
 	 * a DRM Memory Manager object which provides an address space pool
Signature · 1 line
2.53.0
author maintainer
#323 lore raw

PATCH 321/744 bpf: tcp: fix double sock release on batch realloc

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Xiang Mei (Microsoft) <[email protected]>

[ Upstream commit 980a813452754f8001704744e92f7aa697c53dd3 ]

bpf_iter_tcp_batch() releases the current batch via bpf_iter_tcp_put_batch(), which drops the socket refs and rewrites each slot with the socket cookie, then grows the batch. cur_sk/end_sk are kept for bpf_iter_tcp_resume(), but on realloc failure the function returns ERR_PTR() before resume runs, leaving cur_sk < end_sk over slots that now hold cookies rather than sock pointers. bpf_iter_tcp_seq_stop() then calls bpf_iter_tcp_put_batch() again and dereferences a cookie as a struct sock.

Empty the batch on the failure path so stop() does not release it again. The sockets were already freed by the first bpf_iter_tcp_put_batch(), so nothing leaks, and a later read() rescans the bucket from the start instead of skipping it. The sibling GFP_NOWAIT failure path still holds real socket references and is left for stop() to release.

  BUG: KASAN: null-ptr-deref in __sock_gen_cookie
  Read of size 8 at addr 0000000000000059 by task exploit
   ...
   __sock_gen_cookie (net/core/sock_diag.c:28)
   bpf_iter_tcp_put_batch (net/ipv4/tcp_ipv4.c:2918)
   bpf_iter_tcp_seq_stop (net/ipv4/tcp_ipv4.c:3270)
   bpf_seq_read (kernel/bpf/bpf_iter.c:205)
   vfs_read (fs/read_write.c:572)
   ksys_read (fs/read_write.c:716)
   do_syscall_64
   entry_SYSCALL_64_after_hwframe
  Kernel panic - not syncing: Fatal exception
Fixes cdec67a489d4 ("bpf: tcp: Make sure iter->batch always contains a full bucket snapshot")
Reported-by [email protected]
---
 net/ipv4/tcp_ipv4.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)
net/ipv4/tcp_ipv4.c +4 -1
index d5cd238358ef04..c7cf086cc6a1eb 100644
@@ -3144,8 +3144,11 @@ static struct sock *bpf_iter_tcp_batch(struct seq_file *seq)
 	bpf_iter_tcp_put_batch(iter);
 	err = bpf_iter_tcp_realloc_batch(iter, expected * 3 / 2,
 					 GFP_USER);
-	if (err)
+	if (err) {
+		iter->cur_sk = 0;
+		iter->end_sk = 0;
 		return ERR_PTR(err);
+	}
 
 	sk = bpf_iter_tcp_resume(seq);
 	if (!sk)
Signature · 1 line
2.53.0
author maintainer
#324 lore raw

PATCH 322/744 net: stmmac: xgmac: fix l4 filter port overwrite on register update

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Nazim Amirul <[email protected]>

[ Upstream commit 9fcf274d93af17396f20cccb63f1d4c17492a000 ]

The XGMAC_L4_ADDR register holds both source and destination port match values. The current implementation overwrites the entire register when configuring either port, so setting one silently erases the other.

Fix this by reading the register first, then masking and updating only the relevant field before writing back.

---
 .../ethernet/stmicro/stmmac/dwxgmac2_core.c   | 28 +++++++++++--------
 1 file changed, 16 insertions(+), 12 deletions(-)
drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c +16 -12
index f02b434bbd505b..52054f31376d50 100644
@@ -1370,36 +1370,40 @@ static int dwxgmac2_config_l4_filter(struct mac_device_info *hw, u32 filter_no,
 		value &= ~XGMAC_L4PEN0;
 	}
 
-	value &= ~(XGMAC_L4SPM0 | XGMAC_L4SPIM0);
-	value &= ~(XGMAC_L4DPM0 | XGMAC_L4DPIM0);
 	if (sa) {
 		value |= XGMAC_L4SPM0;
 		if (inv)
 			value |= XGMAC_L4SPIM0;
+		else
+			value &= ~XGMAC_L4SPIM0;
 	} else {
 		value |= XGMAC_L4DPM0;
 		if (inv)
 			value |= XGMAC_L4DPIM0;
+		else
+			value &= ~XGMAC_L4DPIM0;
 	}
 
 	ret = dwxgmac2_filter_write(hw, filter_no, XGMAC_L3L4_CTRL, value);
 	if (ret)
 		return ret;
 
-	if (sa) {
-		value = FIELD_PREP(XGMAC_L4SP0, match);
+	ret = dwxgmac2_filter_read(hw, filter_no, XGMAC_L4_ADDR, &value);
+	if (ret)
+		return ret;
 
-		ret = dwxgmac2_filter_write(hw, filter_no, XGMAC_L4_ADDR, value);
-		if (ret)
-			return ret;
+	if (sa) {
+		value &= ~XGMAC_L4SP0;
+		value |= FIELD_PREP(XGMAC_L4SP0, match);
 	} else {
-		value = FIELD_PREP(XGMAC_L4DP0, match);
-
-		ret = dwxgmac2_filter_write(hw, filter_no, XGMAC_L4_ADDR, value);
-		if (ret)
-			return ret;
+		value &= ~XGMAC_L4DP0;
+		value |= FIELD_PREP(XGMAC_L4DP0, match);
 	}
 
+	ret = dwxgmac2_filter_write(hw, filter_no, XGMAC_L4_ADDR, value);
+	if (ret)
+		return ret;
+
 	if (!en)
 		return dwxgmac2_filter_write(hw, filter_no, XGMAC_L3L4_CTRL, 0);
 
Signature · 1 line
2.53.0
author maintainer
#325 lore raw

PATCH 323/744 net: stmmac: fix l3l4 filter rejecting unsupported offload requests

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Nazim Amirul <[email protected]>

[ Upstream commit 5536d7c843637e9430279b94935fcf7df98babb3 ]

The basic flow parser in tc_add_basic_flow() does not validate match keys before proceeding. Unsupported offload configurations such as partial protocol masks, non-IPv4 network proto, or non-TCP/UDP transport proto are silently accepted instead of returning -EOPNOTSUPP.

Add validation to return -EOPNOTSUPP early for:
- No network or transport proto present in the key
- Partial protocol mask (only full mask supported)
- Network proto is not IPv4
- Transport proto is not TCP or UDP

Each rejection includes an extack message so the user knows which part of the match is unsupported.

Also propagate -EOPNOTSUPP from tc_add_basic_flow() in tc_add_flow() by returning it directly rather than using break. The break was silently discarding the error for FLOW_CLS_REPLACE operations where entry->in_use is already true, causing tc_add_flow() to return 0 (success) for unsupported replace requests.

---
 .../net/ethernet/stmicro/stmmac/stmmac_tc.c   | 34 +++++++++++++++++++
 1 file changed, 34 insertions(+)
drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c +34 -0
index d786527185999d..1f8c9f47306b97 100644
@@ -446,6 +446,7 @@ static int tc_parse_flow_actions(struct stmmac_priv *priv,
 }
 
 #define ETHER_TYPE_FULL_MASK	cpu_to_be16(~0)
+#define IP_PROTO_FULL_MASK	0xFF
 
 static int tc_add_basic_flow(struct stmmac_priv *priv,
 			     struct flow_cls_offload *cls,
@@ -461,6 +462,37 @@ static int tc_add_basic_flow(struct stmmac_priv *priv,
 
 	flow_rule_match_basic(rule, &match);
 
+	/* Both network proto and transport proto not present in the key */
+	if (!match.mask || !(match.mask->n_proto || match.mask->ip_proto)) {
+		NL_SET_ERR_MSG_MOD(cls->common.extack,
+				   "filter must specify network or transport protocol");
+		return -EOPNOTSUPP;
+	}
+
+	/* If the proto is present in the key and is not full mask */
+	if ((match.mask->n_proto && match.mask->n_proto != ETHER_TYPE_FULL_MASK) ||
+	    (match.mask->ip_proto && match.mask->ip_proto != IP_PROTO_FULL_MASK)) {
+		NL_SET_ERR_MSG_MOD(cls->common.extack,
+				   "only full protocol mask is supported");
+		return -EOPNOTSUPP;
+	}
+
+	/* Network proto is present in the key and is not IPv4 */
+	if (match.mask->n_proto && match.key->n_proto != cpu_to_be16(ETH_P_IP)) {
+		NL_SET_ERR_MSG_MOD(cls->common.extack,
+				   "only IPv4 network protocol is supported");
+		return -EOPNOTSUPP;
+	}
+
+	/* Transport proto is present in the key and is not TCP or UDP */
+	if (match.mask->ip_proto &&
+	    match.key->ip_proto != IPPROTO_TCP &&
+	    match.key->ip_proto != IPPROTO_UDP) {
+		NL_SET_ERR_MSG_MOD(cls->common.extack,
+				   "only TCP and UDP transport protocols are supported");
+		return -EOPNOTSUPP;
+	}
+
 	entry->ip_proto = match.key->ip_proto;
 	return 0;
 }
@@ -598,6 +630,8 @@ static int tc_add_flow(struct stmmac_priv *priv,
 		ret = tc_flow_parsers[i].fn(priv, cls, entry);
 		if (!ret)
 			entry->in_use = true;
+		else if (ret == -EOPNOTSUPP)
+			return ret;
 	}
 
 	if (!entry->in_use)
Signature · 1 line
2.53.0
author maintainer
#326 lore raw

PATCH 324/744 net: stmmac: reset residual action in L3L4 filters on delete

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Nazim Amirul <[email protected]>

[ Upstream commit a448f821289934b961dd9d8d0beb006cc8937ba2 ]

When deleting an L3/L4 flower filter entry, the action field is not reset. If a filter was previously configured with a drop action, that action may persist and affect subsequent filter configurations unintentionally.

Clear the action field when the filter entry is deleted.

---
 drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c | 1 +
 1 file changed, 1 insertion(+)
drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c +1 -0
index 1f8c9f47306b97..14cabe76e53ec8 100644
@@ -661,6 +661,7 @@ static int tc_del_flow(struct stmmac_priv *priv,
 	entry->in_use = false;
 	entry->cookie = 0;
 	entry->is_l4 = false;
+	entry->action = 0;
 	return ret;
 }
 
Signature · 1 line
2.53.0
author maintainer
#327 lore raw

PATCH 325/744 net: stmmac: enable the MAC on link up for all supported speeds

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: vadik likholetov <[email protected]>

[ Upstream commit 9c99db3a2080b8c2cbbb1100369586a9bea43321 ]

stmmac_mac_link_down() clears the MAC's transmit and receive enable bits. stmmac_mac_link_up() is expected to set them again through stmmac_mac_set(..., true), but it first switches on the negotiated speed and returns early for a speed the switch does not list. The MAC is then left gated off.

The speed selection is split into three switches, keyed on the interface. The generic branch -- taken for everything that is neither USXGMII nor XLGMII, so including PHY_INTERFACE_MODE_10GBASER -- lists only SPEED_2500, SPEED_1000, SPEED_100 and SPEED_10.

MGBE on Tegra234 runs 10GBASE-R into an Aquantia AQR113C. That PHY does rate matching, so phylink_link_up() replaces the media speed with the MAC-side interface speed before calling into the MAC:

	case RATE_MATCH_PAUSE:
		speed = phylink_interface_max_speed(link_state.interface);
		duplex = DUPLEX_FULL;

The driver is therefore called as

	stmmac_mac_link_up(interface=10GBASER, speed=10000, duplex=1)

which falls through to "default: return;". The interface stops passing traffic after the first link flap.

The failure is easy to misread. The link still comes up, because the PHY is polled over MDIO and needs no MAC, so the interface reports carrier 1 at the media speed. The DMA is untouched, so its start bits stay set and descriptors are still consumed. Only the MAC itself is gated off: the receiver counts nothing (mmc_rx_framecount_gb stops advancing, RE is 0) and nothing reaches the wire (TE is 0). The interface survives boot only because stmmac_hw_setup(), called from ndo_open, enables the MAC unconditionally -- so the problem appears only once the cable has been unplugged and plugged back in, and "ip link set dev <ethX> down && ip link set dev <ethX> up" appears to fix it.

The interface is not what the speed bits depend on: with the single exception of 2.5G, which is selected through the XGMII block on USXGMII and through the regular speed bits otherwise, each speed maps to one field of struct mac_link. The per-interface switches are speed validation, and phylink already validates the speed against priv->hw->link.caps. So collapse the three switches into one keyed on the speed alone, keeping the interface test only for the 2.5G case. This covers 10G on 10GBASE-R, and equally 5G, and 1G/100/10 on USXGMII, all of which hit "default: return;" today.

A core that does not support a speed leaves the corresponding mac_link field at 0, and phylink will not offer it that speed in the first place. For dwxgmac2 at 10G, link.xgmii.speed10000 is XGMAC_CONFIG_SS_10000, which is 0 and is the correct speed selection for a 10GBASE-R MAC: ctrl then equals old_ctrl, the register write is skipped, and execution reaches stmmac_mac_set(..., true).

Log an error in the default case, since a speed with no entry here leaves the MAC disabled and the symptom does not point at the cause.

---
 .../net/ethernet/stmicro/stmmac/stmmac_main.c | 92 ++++++++-----------
 1 file changed, 37 insertions(+), 55 deletions(-)
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +37 -55
index 50dfdf5bd3eb02..755f48a34314b1 100644
@@ -1083,63 +1083,45 @@ static void stmmac_mac_link_up(struct phylink_config *config,
 	old_ctrl = readl(priv->ioaddr + MAC_CTRL_REG);
 	ctrl = old_ctrl & ~priv->hw->link.speed_mask;
 
-	if (interface == PHY_INTERFACE_MODE_USXGMII) {
-		switch (speed) {
-		case SPEED_10000:
-			ctrl |= priv->hw->link.xgmii.speed10000;
-			break;
-		case SPEED_5000:
-			ctrl |= priv->hw->link.xgmii.speed5000;
-			break;
-		case SPEED_2500:
+	switch (speed) {
+	case SPEED_100000:
+		ctrl |= priv->hw->link.xlgmii.speed100000;
+		break;
+	case SPEED_50000:
+		ctrl |= priv->hw->link.xlgmii.speed50000;
+		break;
+	case SPEED_40000:
+		ctrl |= priv->hw->link.xlgmii.speed40000;
+		break;
+	case SPEED_25000:
+		ctrl |= priv->hw->link.xlgmii.speed25000;
+		break;
+	case SPEED_10000:
+		ctrl |= priv->hw->link.xgmii.speed10000;
+		break;
+	case SPEED_5000:
+		ctrl |= priv->hw->link.xgmii.speed5000;
+		break;
+	case SPEED_2500:
+		if (interface == PHY_INTERFACE_MODE_USXGMII)
 			ctrl |= priv->hw->link.xgmii.speed2500;
-			break;
-		default:
-			return;
-		}
-	} else if (interface == PHY_INTERFACE_MODE_XLGMII) {
-		switch (speed) {
-		case SPEED_100000:
-			ctrl |= priv->hw->link.xlgmii.speed100000;
-			break;
-		case SPEED_50000:
-			ctrl |= priv->hw->link.xlgmii.speed50000;
-			break;
-		case SPEED_40000:
-			ctrl |= priv->hw->link.xlgmii.speed40000;
-			break;
-		case SPEED_25000:
-			ctrl |= priv->hw->link.xlgmii.speed25000;
-			break;
-		case SPEED_10000:
-			ctrl |= priv->hw->link.xgmii.speed10000;
-			break;
-		case SPEED_2500:
-			ctrl |= priv->hw->link.speed2500;
-			break;
-		case SPEED_1000:
-			ctrl |= priv->hw->link.speed1000;
-			break;
-		default:
-			return;
-		}
-	} else {
-		switch (speed) {
-		case SPEED_2500:
+		else
 			ctrl |= priv->hw->link.speed2500;
-			break;
-		case SPEED_1000:
-			ctrl |= priv->hw->link.speed1000;
-			break;
-		case SPEED_100:
-			ctrl |= priv->hw->link.speed100;
-			break;
-		case SPEED_10:
-			ctrl |= priv->hw->link.speed10;
-			break;
-		default:
-			return;
-		}
+		break;
+	case SPEED_1000:
+		ctrl |= priv->hw->link.speed1000;
+		break;
+	case SPEED_100:
+		ctrl |= priv->hw->link.speed100;
+		break;
+	case SPEED_10:
+		ctrl |= priv->hw->link.speed10;
+		break;
+	default:
+		netdev_err(priv->dev,
+			   "unsupported speed %s on %s, leaving the MAC disabled\n",
+			   phy_speed_to_str(speed), phy_modes(interface));
+		return;
 	}
 
 	if (priv->plat->fix_mac_speed)
Signature · 1 line
2.53.0
author maintainer
#328 lore raw

PATCH 326/744 net: gre: fix lltx regression for GRE tunnels with SEQ/CSUM

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Yun Zhou <[email protected]>

[ Upstream commit 675ed582c1aa4d919dd535490de08c015005c653 ]

Before commit 00d066a4d4ed ("netdev_features: convert NETIF_F_LLTX to dev->lltx"), NETIF_F_LLTX was set unconditionally in both __gre_tunnel_init() and ip6gre_tnl_init_features() alongside GRE_FEATURES:

    dev->features |= GRE_FEATURES | NETIF_F_LLTX;

When that commit converted NETIF_F_LLTX to the dev->lltx flag, it placed 'dev->lltx = true' after the SEQ/CSUM early returns instead of before them. This causes GRE/GRETAP/ip6gre tunnels with SEQ or CSUM+encap to lose lockless TX, reintroducing _xmit_lock acquisition around their ndo_start_xmit. Since GRE xmit re-enters the stack via ip_tunnel_xmit(), holding _xmit_lock risks ABBA deadlock with the underlay device.

  CPU0                        CPU1
  ----                        ----
  lock(&qdisc_xmit_lock_key#6);
                              lock(&qdisc_xmit_lock_key#3);
                              lock(&qdisc_xmit_lock_key#6);
  lock(&qdisc_xmit_lock_key#3);

Fix by moving dev->lltx = true before the early returns in both functions, restoring the original unconditional behavior.

Fixes 00d066a4d4ed ("netdev_features: convert NETIF_F_LLTX to dev->lltx")
---
 net/ipv4/ip_gre.c  | 4 ++--
 net/ipv6/ip6_gre.c | 4 ++--
 2 files changed, 4 insertions(+), 4 deletions(-)
net/ipv4/ip_gre.c +2 -2
index 0ebed1438f6c51..739b7fae8d3da2 100644
@@ -1018,6 +1018,8 @@ static void __gre_tunnel_init(struct net_device *dev)
 	dev->features		|= GRE_FEATURES;
 	dev->hw_features	|= GRE_FEATURES;
 
+	dev->lltx = true;
+
 	/* TCP offload with GRE SEQ is not supported, nor can we support 2
 	 * levels of outer headers requiring an update.
 	 */
@@ -1029,8 +1031,6 @@ static void __gre_tunnel_init(struct net_device *dev)
 
 	dev->features |= NETIF_F_GSO_SOFTWARE;
 	dev->hw_features |= NETIF_F_GSO_SOFTWARE;
-
-	dev->lltx = true;
 }
 
 static int ipgre_tunnel_init(struct net_device *dev)
net/ipv6/ip6_gre.c +2 -2
index 8ebc99a299c954..1a81e80a124aac 100644
@@ -1455,6 +1455,8 @@ static void ip6gre_tnl_init_features(struct net_device *dev)
 	dev->features		|= GRE6_FEATURES;
 	dev->hw_features	|= GRE6_FEATURES;
 
+	dev->lltx = true;
+
 	/* TCP offload with GRE SEQ is not supported, nor can we support 2
 	 * levels of outer headers requiring an update.
 	 */
@@ -1466,8 +1468,6 @@ static void ip6gre_tnl_init_features(struct net_device *dev)
 
 	dev->features |= NETIF_F_GSO_SOFTWARE;
 	dev->hw_features |= NETIF_F_GSO_SOFTWARE;
-
-	dev->lltx = true;
 }
 
 static int ip6gre_tunnel_init_common(struct net_device *dev)
Signature · 1 line
2.53.0
author maintainer
#329 lore raw

PATCH 327/744 mpls: Set rt->rt_nhn just before returning from mpls_nh_build_multi().

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Kuniyuki Iwashima <[email protected]>

[ Upstream commit 3671f0419d90b98a02f313830595ab958c8b2025 ]

Commit f0914b8436c5 ("mpls: Hold dev refcnt for mpls_nh.") added change_nexthops() loop to call netdev_put() for the nexthop devices before freeing mpls_route.

Then, mpls_nh_build_multi() was also changed to avoid iterating uninitialised nexthops in mpls_rt_free_rcu().

However, setting rt->rt_nhn to 0 at the entry of mpls_nh_build_multi() makes the following change_nexthops() no-op.

Let's set rt->rt_nhn just before returning from mpls_nh_build_multi().

---
 net/mpls/af_mpls.c | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)
net/mpls/af_mpls.c +5 -3
index b64a8b1bac0961..35069183f59c3e 100644
@@ -922,8 +922,7 @@ static int mpls_nh_build_multi(struct mpls_route_config *cfg,
 	struct nlattr *nla_via, *nla_newdst;
 	int remaining = cfg->rc_mp_len;
 	int err = 0;
-
-	rt->rt_nhn = 0;
+	u8 nhs = 0;
 
 	change_nexthops(rt) {
 		int attrlen;
@@ -959,12 +958,15 @@ static int mpls_nh_build_multi(struct mpls_route_config *cfg,
 			rt->rt_nhn_alive--;
 
 		rtnh = rtnh_next(rtnh, &remaining);
-		rt->rt_nhn++;
+		nhs++;
 	} endfor_nexthops(rt);
 
+	rt->rt_nhn = nhs;
+
 	return 0;
 
 errout:
+	rt->rt_nhn = nhs;
 	return err;
 }
 
Signature · 1 line
2.53.0
author maintainer
#330 lore raw

PATCH 328/744 octeontx2-vf: set TC flower flag on MCAM entry allocation

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Suman Ghosh <[email protected]>

[ Upstream commit 0d4d31e3cc5dd6204fa1495c4107f5075acce5ed ]

When MCAM entries are allocated for a VF netdev via the devlink mcam_count parameter, only OTX2_FLAG_NTUPLE_SUPPORT was set. That enabled ethtool ntuple filters but not tc flower offload. Also set OTX2_FLAG_TC_FLOWER_SUPPORT when entries are successfully allocated.

Fixes 2da489432747 ("octeontx2-pf: devlink params support to set mcam entry count")
---
 drivers/net/ethernet/marvell/octeontx2/nic/otx2_flows.c | 1 +
 1 file changed, 1 insertion(+)
drivers/net/ethernet/marvell/octeontx2/nic/otx2_flows.c +1 -0
index 38cc539d724d84..3b3eee15715f75 100644
@@ -270,6 +270,7 @@ int otx2_alloc_mcam_entries(struct otx2_nic *pfvf, u16 count)
 	if (allocated) {
 		pfvf->flags |= OTX2_FLAG_MCAM_ENTRIES_ALLOC;
 		pfvf->flags |= OTX2_FLAG_NTUPLE_SUPPORT;
+		pfvf->flags |= OTX2_FLAG_TC_FLOWER_SUPPORT;
 	}
 
 	if (allocated != count)
Signature · 1 line
2.53.0
author maintainer
#331 lore raw

PATCH 329/744 ipv4: icmp: fill flow parameters in icmp_route_lookup decoy lookup

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Eric Dumazet <[email protected]>

[ Upstream commit 853e164c2b321f0711361bc23505aaeb7dc432c3 ]

When Linux forwards a packet and needs to generate an ICMP error, icmp_route_lookup() performs a reverse-path relookup. For non-local destinations, it performs a decoy lookup to find the expected egress interface (rt2->dst.dev) before validating the path with ip_route_input().

Currently, the decoy flow structure (fl4_2) only sets .daddr = fl4_dec.saddr, leaving .saddr, .flowi4_dscp, .flowi4_proto, .flowi4_mark, .flowi4_oif, .fl4_sport, .fl4_dport, and .flowi4_uid zeroed out.

When policy routing rules (such as ip rule add from $SRC lookup 100, or dscp/fwmark/ipproto/port rules, or VRF bindings) are configured:
1. The decoy lookup fails to match the policy rule because saddr and other key flow selectors are missing in fl4_2.
2. It resolves a route using the default table instead, returning an incorrect egress netdev.
3. Passing the wrong netdev to ip_route_input() causes strict reverse-path filtering (rp_filter=1) to fail, logging false-positive "martian source" warnings and causing the relookup to fail.

Fix this by initializing fl4_2 from fl4_dec and:
- Swapping source/destination IP addresses.
- Swapping L4 ports for transport protocols with ports (TCP, UDP, SCTP, DCCP) so port-based policy routing matches correctly. Non-port protocols (such as ICMP or GRE) leave the flowi_uli union fields intact to prevent corruption.
- Setting .flowi4_oif = l3mdev_master_ifindex(route_lookup_dev) to ensure VRF routing tables are respected.
- Setting .flowi4_flags |= FLOWI_FLAG_ANYSRC to allow output route lookups for non-local source IP addresses.
- Using __ip_route_output_key() instead of ip_route_output_key() for fl4_2 so that raw FIB routing is used without triggering spurious XFRM policy lookups on the decoy flow (the actual XFRM lookup is performed later using fl4_dec).

---
 net/ipv4/icmp.c | 18 +++++++++++++++---
 1 file changed, 15 insertions(+), 3 deletions(-)
net/ipv4/icmp.c +15 -3
index 23e921d313b36b..0caedfc7ca92f6 100644
@@ -548,11 +548,23 @@ static struct rtable *icmp_route_lookup(struct net *net, struct flowi4 *fl4,
 		if (IS_ERR(rt2))
 			err = PTR_ERR(rt2);
 	} else {
-		struct flowi4 fl4_2 = {};
+		struct flowi4 fl4_2 = fl4_dec;
 		unsigned long orefdst;
 
-		fl4_2.daddr = fl4_dec.saddr;
-		rt2 = ip_route_output_key(net, &fl4_2);
+		swap(fl4_2.daddr, fl4_2.saddr);
+		switch (fl4_2.flowi4_proto) {
+		case IPPROTO_TCP:
+		case IPPROTO_UDP:
+		case IPPROTO_SCTP:
+		case IPPROTO_DCCP:
+			swap(fl4_2.fl4_sport, fl4_2.fl4_dport);
+			break;
+		}
+
+		fl4_2.flowi4_oif = l3mdev_master_ifindex(route_lookup_dev);
+		fl4_2.flowi4_flags |= FLOWI_FLAG_ANYSRC;
+
+		rt2 = __ip_route_output_key(net, &fl4_2);
 		if (IS_ERR(rt2)) {
 			err = PTR_ERR(rt2);
 			goto relookup_failed;
Signature · 1 line
2.53.0
author maintainer
#332 lore raw

PATCH 330/744 ppp: annotate data races in ppp_generic

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Eric Dumazet <[email protected]>

[ Upstream commit 543adf072165aaf2e3b635c0476204f9658ed3bf ]

Several fields in struct ppp can be read or updated concurrently from multiple CPUs without synchronization, causing data races:

1. ppp->mru is read concurrently in ppp_receive_nonmp_frame() while being updated via PPPIOCSMRU ioctl. Protect ppp->mru updates in PPPIOCSMRU with ppp_recv_lock(ppp).

2. PPPIOCGFLAGS reads ppp->flags, ppp->xstate, and ppp->rstate unlocked. Wrap the read in ppp_lock(ppp) to get a consistent snapshot.

3. ppp->debug is updated via PPPIOCSDEBUG and read concurrently on fast paths. Annotate reads with READ_ONCE() and writes with WRITE_ONCE().

4. ppp->last_xmit and ppp->last_recv are updated on TX/RX data paths and read via PPPIOCGIDLE32 / PPPIOCGIDLE64 ioctls. Annotate with WRITE_ONCE() / READ_ONCE() and use max() to handle jiffies subtraction.

5. ppp->npmode[] is updated via PPPIOCSNPMODE and read on TX/RX paths. Annotate with WRITE_ONCE() / READ_ONCE().

---
 drivers/net/ppp/ppp_generic.c | 50 ++++++++++++++++++++---------------
 1 file changed, 28 insertions(+), 22 deletions(-)
drivers/net/ppp/ppp_generic.c +28 -22
index ef54e0a0462a17..cacc4c3a37d2cd 100644
@@ -810,7 +810,9 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 	case PPPIOCSMRU:
 		if (get_user(val, p))
 			break;
+		ppp_recv_lock(ppp);
 		ppp->mru = val;
+		ppp_recv_unlock(ppp);
 		err = 0;
 		break;
 
@@ -831,7 +833,9 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 		break;
 
 	case PPPIOCGFLAGS:
+		ppp_lock(ppp);
 		val = ppp->flags | ppp->xstate | ppp->rstate;
+		ppp_unlock(ppp);
 		if (put_user(val, p))
 			break;
 		err = 0;
@@ -855,7 +859,7 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 	case PPPIOCSDEBUG:
 		if (get_user(val, p))
 			break;
-		ppp->debug = val;
+		WRITE_ONCE(ppp->debug, val);
 		err = 0;
 		break;
 
@@ -866,16 +870,16 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 		break;
 
 	case PPPIOCGIDLE32:
-                idle32.xmit_idle = (jiffies - ppp->last_xmit) / HZ;
-                idle32.recv_idle = (jiffies - ppp->last_recv) / HZ;
-                if (copy_to_user(argp, &idle32, sizeof(idle32)))
+		idle32.xmit_idle = max(0L, (long)(jiffies - READ_ONCE(ppp->last_xmit))) / HZ;
+		idle32.recv_idle = max(0L, (long)(jiffies - READ_ONCE(ppp->last_recv))) / HZ;
+		if (copy_to_user(argp, &idle32, sizeof(idle32)))
 			break;
 		err = 0;
 		break;
 
 	case PPPIOCGIDLE64:
-		idle64.xmit_idle = (jiffies - ppp->last_xmit) / HZ;
-		idle64.recv_idle = (jiffies - ppp->last_recv) / HZ;
+		idle64.xmit_idle = max(0L, (long)(jiffies - READ_ONCE(ppp->last_xmit))) / HZ;
+		idle64.recv_idle = max(0L, (long)(jiffies - READ_ONCE(ppp->last_recv))) / HZ;
 		if (copy_to_user(argp, &idle64, sizeof(idle64)))
 			break;
 		err = 0;
@@ -916,7 +920,7 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 			if (copy_to_user(argp, &npi, sizeof(npi)))
 				break;
 		} else {
-			ppp->npmode[i] = npi.mode;
+			WRITE_ONCE(ppp->npmode[i], npi.mode);
 			/* we may be able to transmit more packets now (??) */
 			netif_wake_queue(ppp->dev);
 		}
@@ -1454,7 +1458,7 @@ ppp_start_xmit(struct sk_buff *skb, struct net_device *dev)
 		goto outf;
 
 	/* Drop, accept or reject the packet */
-	switch (ppp->npmode[npi]) {
+	switch (READ_ONCE(ppp->npmode[npi])) {
 	case NPMODE_PASS:
 		break;
 	case NPMODE_QUEUE:
@@ -1790,7 +1794,7 @@ ppp_prepare_tx_skb(struct ppp *ppp, struct sk_buff **pskb)
 		*(__be16 *)skb_push(skb, 2) = htons(PPP_FILTER_OUTBOUND_TAG);
 		if (ppp->pass_filter &&
 		    bpf_prog_run(ppp->pass_filter, skb) == 0) {
-			if (ppp->debug & 1)
+			if (READ_ONCE(ppp->debug) & 1)
 				netdev_printk(KERN_DEBUG, ppp->dev,
 					      "PPP: outbound frame "
 					      "not passed\n");
@@ -1800,11 +1804,11 @@ ppp_prepare_tx_skb(struct ppp *ppp, struct sk_buff **pskb)
 		/* if this packet passes the active filter, record the time */
 		if (!(ppp->active_filter &&
 		      bpf_prog_run(ppp->active_filter, skb) == 0))
-			ppp->last_xmit = jiffies;
+			WRITE_ONCE(ppp->last_xmit, jiffies);
 		skb_pull(skb, 2);
 #else
 		/* for data packets, record the time */
-		ppp->last_xmit = jiffies;
+		WRITE_ONCE(ppp->last_xmit, jiffies);
 #endif /* CONFIG_PPP_FILTER */
 	}
 
@@ -2154,7 +2158,7 @@ static int ppp_mp_explode(struct ppp *ppp, struct sk_buff *skb)
  noskb:
 	spin_unlock(&pch->downl);
  err_linearize:
-	if (ppp->debug & 1)
+	if (READ_ONCE(ppp->debug) & 1)
 		netdev_err(ppp->dev, "PPP: no memory (fragment)\n");
 	DEV_STATS_INC(ppp->dev, tx_errors);
 	++ppp->nxseq;
@@ -2502,7 +2506,7 @@ ppp_receive_nonmp_frame(struct ppp *ppp, struct sk_buff *skb)
 			*(__be16 *)skb_push(skb, 2) = htons(PPP_FILTER_INBOUND_TAG);
 			if (ppp->pass_filter &&
 			    bpf_prog_run(ppp->pass_filter, skb) == 0) {
-				if (ppp->debug & 1)
+				if (READ_ONCE(ppp->debug) & 1)
 					netdev_printk(KERN_DEBUG, ppp->dev,
 						      "PPP: inbound frame "
 						      "not passed\n");
@@ -2511,14 +2515,14 @@ ppp_receive_nonmp_frame(struct ppp *ppp, struct sk_buff *skb)
 			}
 			if (!(ppp->active_filter &&
 			      bpf_prog_run(ppp->active_filter, skb) == 0))
-				ppp->last_recv = jiffies;
+				WRITE_ONCE(ppp->last_recv, jiffies);
 			__skb_pull(skb, 2);
 		} else
 #endif /* CONFIG_PPP_FILTER */
-			ppp->last_recv = jiffies;
+			WRITE_ONCE(ppp->last_recv, jiffies);
 
 		if ((ppp->dev->flags & IFF_UP) == 0 ||
-		    ppp->npmode[npi] != NPMODE_PASS) {
+		    READ_ONCE(ppp->npmode[npi]) != NPMODE_PASS) {
 			kfree_skb(skb);
 		} else {
 			/* chop off protocol */
@@ -2771,7 +2775,7 @@ ppp_mp_reconstruct(struct ppp *ppp)
 			seq = seq_before(minseq, PPP_MP_CB(p)->sequence)?
 				minseq + 1: PPP_MP_CB(p)->sequence;
 
-			if (ppp->debug & 1)
+			if (READ_ONCE(ppp->debug) & 1)
 				netdev_printk(KERN_DEBUG, ppp->dev,
 					      "lost frag %u..%u\n",
 					      oldseq, seq-1);
@@ -2820,7 +2824,7 @@ ppp_mp_reconstruct(struct ppp *ppp)
 			struct sk_buff *tmp2;
 
 			skb_queue_reverse_walk_from_safe(list, p, tmp2) {
-				if (ppp->debug & 1)
+				if (READ_ONCE(ppp->debug) & 1)
 					netdev_printk(KERN_DEBUG, ppp->dev,
 						      "discarding frag %u\n",
 						      PPP_MP_CB(p)->sequence);
@@ -2842,7 +2846,7 @@ ppp_mp_reconstruct(struct ppp *ppp)
 			skb_queue_walk_safe(list, p, tmp) {
 				if (p == head)
 					break;
-				if (ppp->debug & 1)
+				if (READ_ONCE(ppp->debug) & 1)
 					netdev_printk(KERN_DEBUG, ppp->dev,
 						      "discarding frag %u\n",
 						      PPP_MP_CB(p)->sequence);
@@ -2850,7 +2854,7 @@ ppp_mp_reconstruct(struct ppp *ppp)
 				kfree_skb(p);
 			}
 
-			if (ppp->debug & 1)
+			if (READ_ONCE(ppp->debug) & 1)
 				netdev_printk(KERN_DEBUG, ppp->dev,
 					      "  missed pkts %u..%u\n",
 					      ppp->nextseq,
@@ -3161,7 +3165,8 @@ ppp_ccp_peek(struct ppp *ppp, struct sk_buff *skb, int inbound)
 			if (!ppp->rc_state)
 				break;
 			if (ppp->rcomp->decomp_init(ppp->rc_state, dp, len,
-					ppp->file.index, 0, ppp->mru, ppp->debug)) {
+						ppp->file.index, 0, ppp->mru,
+						READ_ONCE(ppp->debug))) {
 				ppp->rstate |= SC_DECOMP_RUN;
 				ppp->rstate &= ~(SC_DC_ERROR | SC_DC_FERROR);
 			}
@@ -3170,7 +3175,8 @@ ppp_ccp_peek(struct ppp *ppp, struct sk_buff *skb, int inbound)
 			if (!ppp->xc_state)
 				break;
 			if (ppp->xcomp->comp_init(ppp->xc_state, dp, len,
-					ppp->file.index, 0, ppp->debug))
+						  ppp->file.index, 0,
+						  READ_ONCE(ppp->debug)))
 				ppp->xstate |= SC_COMP_RUN;
 		}
 		break;
Signature · 1 line
2.53.0
author maintainer
#333 lore raw

PATCH 331/744 hinic: remove unused ethtool RSS user configuration buffers

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Chenguang Zhao <[email protected]>

[ Upstream commit fe0c002928c6749b7f4a726f6f600f6dd70280ea ]

rss_indir_user and rss_hkey_user are allocated and filled in __set_rss_rxfh() when the user configures RSS via ethtool, but nothing ever reads them. hinic_get_rxfh() fetches the state from the device, and the hardware is programmed from the original indir/key arguments. These buffers only leaked on driver unload.

Drop the unused allocations, memcpys, and struct fields.

Fixes 4fdc51bb4e92 ("hinic: add support for rss parameters with ethtool")
---
 drivers/net/ethernet/huawei/hinic/hinic_dev.h |  2 --
 .../net/ethernet/huawei/hinic/hinic_ethtool.c | 21 -------------------
 2 files changed, 23 deletions(-)
drivers/net/ethernet/huawei/hinic/hinic_dev.h +0 -2
index 52ea97c818b8ec..d9ab94910a2a79 100644
@@ -104,8 +104,6 @@ struct hinic_dev {
 	u16				num_rss;
 	u16				rss_limit;
 	struct hinic_rss_type		rss_type;
-	u8				*rss_hkey_user;
-	s32				*rss_indir_user;
 	struct hinic_intr_coal_info	*rx_intr_coalesce;
 	struct hinic_intr_coal_info	*tx_intr_coalesce;
 	struct hinic_sriov_info sriov_info;
drivers/net/ethernet/huawei/hinic/hinic_ethtool.c +0 -21
index a8b129ce1b7e9f..f75e8563f23aee 100644
@@ -1064,17 +1064,6 @@ static int __set_rss_rxfh(struct net_device *netdev,
 	int err;
 
 	if (indir) {
-		if (!nic_dev->rss_indir_user) {
-			nic_dev->rss_indir_user =
-				kzalloc(sizeof(u32) * HINIC_RSS_INDIR_SIZE,
-					GFP_KERNEL);
-			if (!nic_dev->rss_indir_user)
-				return -ENOMEM;
-		}
-
-		memcpy(nic_dev->rss_indir_user, indir,
-		       sizeof(u32) * HINIC_RSS_INDIR_SIZE);
-
 		err = hinic_rss_set_indir_tbl(nic_dev,
 					      nic_dev->rss_tmpl_idx, indir);
 		if (err)
@@ -1082,16 +1071,6 @@ static int __set_rss_rxfh(struct net_device *netdev,
 	}
 
 	if (key) {
-		if (!nic_dev->rss_hkey_user) {
-			nic_dev->rss_hkey_user =
-				kzalloc(HINIC_RSS_KEY_SIZE * 2, GFP_KERNEL);
-
-			if (!nic_dev->rss_hkey_user)
-				return -ENOMEM;
-		}
-
-		memcpy(nic_dev->rss_hkey_user, key, HINIC_RSS_KEY_SIZE);
-
 		err = hinic_rss_set_template_tbl(nic_dev,
 						 nic_dev->rss_tmpl_idx, key);
 		if (err)
Signature · 1 line
2.53.0
author maintainer
#334 lore raw

PATCH 332/744 LoongArch: BPF: Zero-extend signed ALU32 div/mod results

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Nicholas Dudar <[email protected]>

[ Upstream commit dacd348b8a993373576fe2ee2d8b114740ba57a6 ]

ALU32 operations write a 32-bit result and leave the upper 32 bits of the BPF register zero. The LoongArch JIT sign-extends the result of signed ALU32 BPF_DIV and BPF_MOD (off=1), so a negative 32-bit quotient or remainder leaves bits 63:32 set in JITted code while the verifier and interpreter model those bits as zero.

Keep sign-extension on the operands, which signed divide needs, and zero-extend the ALU32 result after the divide or modulo instruction, matching the unsigned ALU32 div/mod paths and every other ALU32 operation in this JIT.

Fixes 2425c9e002d2 ("LoongArch: BPF: Support signed div instructions")
Fixes 7b6b13d32965 ("LoongArch: BPF: Support signed mod instructions")
Assisted-by Claude:claude-opus-4-8
---
 arch/loongarch/net/bpf_jit.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)
arch/loongarch/net/bpf_jit.c +4 -4
index e4966c178abc56..22c03c02dae6ed 100644
@@ -834,7 +834,7 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx, bool ext
 			move_reg(ctx, t1, src);
 			emit_sext_32(ctx, t1, is32);
 			emit_insn(ctx, divd, dst, dst, t1);
-			emit_sext_32(ctx, dst, is32);
+			emit_zext_32(ctx, dst, is32);
 		}
 		break;
 
@@ -851,7 +851,7 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx, bool ext
 			emit_sext_32(ctx, t1, is32);
 			emit_sext_32(ctx, dst, is32);
 			emit_insn(ctx, divd, dst, dst, t1);
-			emit_sext_32(ctx, dst, is32);
+			emit_zext_32(ctx, dst, is32);
 		}
 		break;
 
@@ -869,7 +869,7 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx, bool ext
 			move_reg(ctx, t1, src);
 			emit_sext_32(ctx, t1, is32);
 			emit_insn(ctx, modd, dst, dst, t1);
-			emit_sext_32(ctx, dst, is32);
+			emit_zext_32(ctx, dst, is32);
 		}
 		break;
 
@@ -886,7 +886,7 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx, bool ext
 			emit_sext_32(ctx, t1, is32);
 			emit_sext_32(ctx, dst, is32);
 			emit_insn(ctx, modd, dst, dst, t1);
-			emit_sext_32(ctx, dst, is32);
+			emit_zext_32(ctx, dst, is32);
 		}
 		break;
 
Signature · 1 line
2.53.0
author maintainer
#335 lore raw

PATCH 333/744 net: qrtr: restrict socket creation to the initial network namespace

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Aldo Ariel Panzardo <[email protected]>

[ Upstream commit 3b536db8fb32da9e9c62f2bb45e2e319331f0426 ]

QRTR keeps its entire port and node state in module-global variables that are not partitioned per network namespace: qrtr_local_nid is a single global node id (always 1) and qrtr_ports is a single global xarray. qrtr_port_lookup() and qrtr_local_enqueue() operate on that global state with no network-namespace check, and qrtr_create() places no restriction on the namespace a socket is created in.

As a result an unprivileged process that creates an AF_QIPCRTR socket in a separate network namespace, e.g. via unshare(CLONE_NEWUSER | CLONE_NEWNET), can send QRTR datagrams - including control-plane messages such as QRTR_TYPE_NEW_SERVER - to QRTR sockets owned by another namespace, and vice versa. The receiving socket sees such a message as coming from node id 1, indistinguishable from a legitimate local client, breaking the isolation that network namespaces are expected to provide.

QRTR is a transport to global hardware endpoints (the modem and other remote processors) and has no per-namespace semantics; its in-kernel name service already creates its socket in init_net only. Confine the socket family to the initial network namespace, as other non-namespace-aware socket families do (see llc_ui_create() and the ieee802154 socket code).

---
 net/qrtr/af_qrtr.c | 8 ++++++++
 1 file changed, 8 insertions(+)
net/qrtr/af_qrtr.c +8 -0
index 2288159f5b1bba..7482a09adb54f9 100644
@@ -1261,6 +1261,14 @@ static int qrtr_create(struct net *net, struct socket *sock,
 	if (sock->type != SOCK_DGRAM)
 		return -EPROTOTYPE;
 
+	/* QRTR keeps its port and node state in module-global variables that
+	 * are not partitioned per network namespace, and the in-kernel name
+	 * service only operates in init_net. Confine the family to init_net so
+	 * a socket in another namespace cannot reach the global control plane.
+	 */
+	if (!net_eq(net, &init_net))
+		return -EAFNOSUPPORT;
+
 	sk = sk_alloc(net, AF_QIPCRTR, GFP_KERNEL, &qrtr_proto, kern);
 	if (!sk)
 		return -ENOMEM;
Signature · 1 line
2.53.0
author maintainer
#336 lore raw

PATCH 334/744 raw: annotate lockless match fields in raw_v4_match()

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Runyu Xiao <[email protected]>

[ Upstream commit 18f116931f52e3c3303ad4b15ff41eb89b0e4239 ]

raw_v4_match() is a lockless match helper under sk_for_each_rcu(). It still reads inet->inet_daddr, inet->inet_rcv_saddr and sk->sk_bound_dev_if with plain loads while bind, connect and bind-to-device paths can update the same match fields concurrently.

Annotate only those mutable match fields in raw_v4_match(), and do so at the point of use instead of hoisting the bound-device read before the earlier short-circuit tests.

Also annotate the raw bind writer and the shared IPv4 datagram connect writer used by raw sockets, so the address fields updated on bind and connect match explicit WRITE_ONCE() updates.

This version intentionally leaves the shared disconnect-side IPv4 writers to follow-up cleanup and limits the writer changes here to the raw bind path and the datagram connect path directly exercised by raw sockets.

Fixes 0daf07e52709 ("raw: convert raw sockets to RCU")
---
 net/ipv4/datagram.c |  4 ++--
 net/ipv4/raw.c      | 23 ++++++++++++++++-------
 2 files changed, 18 insertions(+), 9 deletions(-)
net/ipv4/datagram.c +2 -2
index 1614593b6d7270..7d25519a6cdd3f 100644
@@ -63,12 +63,12 @@ int __ip4_datagram_connect(struct sock *sk, struct sockaddr_unsized *uaddr, int
 	}
 
 	/* Update addresses before rehashing */
-	inet->inet_daddr = fl4->daddr;
+	WRITE_ONCE(inet->inet_daddr, fl4->daddr);
 	inet->inet_dport = usin->sin_port;
 	if (!inet->inet_saddr)
 		inet->inet_saddr = fl4->saddr;
 	if (!inet->inet_rcv_saddr) {
-		inet->inet_rcv_saddr = fl4->saddr;
+		WRITE_ONCE(inet->inet_rcv_saddr, fl4->saddr);
 		if (sk->sk_prot->rehash)
 			sk->sk_prot->rehash(sk);
 	}
net/ipv4/raw.c +16 -7
index 68e88cb3e55cbd..f47b8b49da3d12 100644
@@ -120,13 +120,21 @@ bool raw_v4_match(struct net *net, const struct sock *sk, unsigned short num,
 		  __be32 raddr, __be32 laddr, int dif, int sdif)
 {
 	const struct inet_sock *inet = inet_sk(sk);
+	__be32 daddr, rcv_saddr;
 
-	if (net_eq(sock_net(sk), net) && inet->inet_num == num	&&
-	    !(inet->inet_daddr && inet->inet_daddr != raddr) 	&&
-	    !(inet->inet_rcv_saddr && inet->inet_rcv_saddr != laddr) &&
-	    raw_sk_bound_dev_eq(net, sk->sk_bound_dev_if, dif, sdif))
-		return true;
-	return false;
+	if (!net_eq(sock_net(sk), net) || inet->inet_num != num)
+		return false;
+
+	daddr = READ_ONCE(inet->inet_daddr);
+	if (daddr && daddr != raddr)
+		return false;
+
+	rcv_saddr = READ_ONCE(inet->inet_rcv_saddr);
+	if (rcv_saddr && rcv_saddr != laddr)
+		return false;
+
+	return raw_sk_bound_dev_eq(net, READ_ONCE(sk->sk_bound_dev_if),
+				   dif, sdif);
 }
 EXPORT_SYMBOL_GPL(raw_v4_match);
 
@@ -724,7 +732,8 @@ static int raw_bind(struct sock *sk, struct sockaddr_unsized *uaddr,
 					 chk_addr_ret))
 		goto out;
 
-	inet->inet_rcv_saddr = inet->inet_saddr = addr->sin_addr.s_addr;
+	inet->inet_saddr = addr->sin_addr.s_addr;
+	WRITE_ONCE(inet->inet_rcv_saddr, addr->sin_addr.s_addr);
 	if (chk_addr_ret == RTN_MULTICAST || chk_addr_ret == RTN_BROADCAST)
 		inet->inet_saddr = 0;  /* Use device */
 	sk_dst_reset(sk);
Signature · 1 line
2.53.0
author maintainer
#337 lore raw

PATCH 335/744 net/mlx5: Fix MCIA register buffer overflow on 32 dword reads

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Gal Pressman <[email protected]>

[ Upstream commit 11c057d23465c7a5817a7284c896d19d54c0b616 ]

The MCIA register can return up to 32 dwords (128 bytes) when the device advertises the mcia_32dwords capability, but struct mlx5_ifc_mcia_reg_bits only defines dword_0..11, leaving room for just 12 dwords (48 bytes) of data.

mlx5_query_mcia() clamps the read size to mlx5_mcia_max_bytes() and then memcpy()s that many bytes out of the register, potentially reading past the end of the 'out' buffer. On kernels built with FORTIFY_SOURCE this is caught as a buffer overflow while reading the module EEPROM via ethtool:

detected buffer overflow in memcpy
kernel BUG at lib/string_helpers.c:1048!

  RIP: 0010:fortify_panic+0x13/0x20
  Call Trace:
   mlx5_query_mcia.isra.0+0x200/0x210 [mlx5_core]
   mlx5_query_module_eeprom_by_page+0x4a/0xa0 [mlx5_core]
   mlx5e_get_module_eeprom_by_page+0xbb/0x120 [mlx5_core]
   eeprom_prepare_data+0xf3/0x170
   ethnl_default_doit+0xf1/0x3b0

Extend the mcia_reg layout to 32 dwords.

Fixes 271907ee2f29 ("net/mlx5: Query the maximum MCIA register read size from firmware")
---
 drivers/net/ethernet/mellanox/mlx5/core/port.c |  4 ++--
 include/linux/mlx5/mlx5_ifc.h                  | 13 +------------
 2 files changed, 3 insertions(+), 14 deletions(-)
drivers/net/ethernet/mellanox/mlx5/core/port.c +2 -2
index ee8b9765c5bafd..2cba370a01d468 100644
@@ -314,7 +314,7 @@ static int mlx5_query_module_id(struct mlx5_core_dev *dev, int module_num,
 		return -EIO;
 	}
 
-	ptr = MLX5_ADDR_OF(mcia_reg, out, dword_0);
+	ptr = MLX5_ADDR_OF(mcia_reg, out, dwords);
 
 	*module_id = ptr[0];
 
@@ -399,7 +399,7 @@ static int mlx5_query_mcia(struct mlx5_core_dev *dev,
 		return -EIO;
 	}
 
-	ptr = MLX5_ADDR_OF(mcia_reg, out, dword_0);
+	ptr = MLX5_ADDR_OF(mcia_reg, out, dwords);
 	memcpy(data, ptr, size);
 
 	return size;
include/linux/mlx5/mlx5_ifc.h +1 -12
index 49f3ad4b1a7c54..dfd98e17fe688d 100644
@@ -12203,18 +12203,7 @@ struct mlx5_ifc_mcia_reg_bits {
 
 	u8         reserved_at_60[0x20];
 
-	u8         dword_0[0x20];
-	u8         dword_1[0x20];
-	u8         dword_2[0x20];
-	u8         dword_3[0x20];
-	u8         dword_4[0x20];
-	u8         dword_5[0x20];
-	u8         dword_6[0x20];
-	u8         dword_7[0x20];
-	u8         dword_8[0x20];
-	u8         dword_9[0x20];
-	u8         dword_10[0x20];
-	u8         dword_11[0x20];
+	u8         dwords[0x400];
 };
 
 struct mlx5_ifc_dcbx_param_bits {
Signature · 1 line
2.53.0
author maintainer
#338 lore raw

PATCH 336/744 net/mlx5: E-Switch, fix zero num_dest in prio_tag egress vlan rule

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Yael Chemla <[email protected]>

[ Upstream commit d12956d083eb70f2c6d72711aebaf8c2ce21e170 ]

esw_egress_acl_vlan_create() hardcodes num_dest=0 in its mlx5_add_flow_rules() call. When invoked from the non-bond path fwd_dest is NULL and num_dest=0 is correct. When invoked from esw_acl_egress_ofld_rules_create() during a bond event, fwd_dest is non-NULL and flow_act.action carries MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, but _mlx5_add_flow_rules() rejects a non-NULL dest pointer paired with dest_num<=0 and returns -EINVAL. The error propagates as "configure slave vport egress fwd, err(-22)". The passive vport's egress ACL table ends up with its flow groups allocated but no FTEs, so prio-tagged packets are not popped and bond failover is broken on prio_tag_required devices.

Fix by passing fwd_dest ? 1 : 0 as num_dest to match the actual number of destinations supplied.

Fixes bf773dc0e6d5 ("net/mlx5: E-Switch, Introduce APIs to enable egress acl forward-to-vport rule")
---
 drivers/net/ethernet/mellanox/mlx5/core/esw/acl/helper.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/ethernet/mellanox/mlx5/core/esw/acl/helper.c +1 -1
index ba5cce706ea269..9693c74e9b16a4 100644
@@ -71,7 +71,7 @@ int esw_egress_acl_vlan_create(struct mlx5_eswitch *esw,
 	flow_act.action = flow_action;
 	vport->egress.allowed_vlan =
 		mlx5_add_flow_rules(vport->egress.acl, spec,
-				    &flow_act, fwd_dest, 0);
+				    &flow_act, fwd_dest, fwd_dest ? 1 : 0);
 	if (IS_ERR(vport->egress.allowed_vlan)) {
 		err = PTR_ERR(vport->egress.allowed_vlan);
 		esw_warn(esw->dev,
Signature · 1 line
2.53.0
author maintainer
#339 lore raw

PATCH 337/744 net/mlx5e: Report zero bandwidth for non-ETS traffic classes

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Alexei Lazar <[email protected]>

[ Upstream commit ffb1873b2df11945b8c395e859169248675c91c5 ]

The IEEE 802.1Qaz standard defines that bandwidth allocation percentages only apply to Enhanced Transmission Selection (ETS) traffic classes. For STRICT and VENDOR transmission selection algorithms, bandwidth percentage values are not applicable.

Currently for non-ETS 100 bandwidth is being reported for all traffic classes in the get operation due to hardware limitation, regardless of their TSA type.

Fix this by reporting 0 for non-ETS traffic classes.

---
 drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c | 7 +++++++
 1 file changed, 7 insertions(+)
drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c +7 -0
index 4b86df6d5b9eaa..762f0a46c12097 100644
@@ -173,6 +173,13 @@ static int mlx5e_dcbnl_ieee_getets(struct net_device *netdev,
 	}
 	memcpy(ets->tc_tsa, priv->dcbx.tc_tsa, sizeof(ets->tc_tsa));
 
+	/* Report 0 for non ETS TSA */
+	for (i = 0; i < ets->ets_cap; i++) {
+		if (ets->tc_tx_bw[i] == MLX5E_MAX_BW_ALLOC &&
+		    priv->dcbx.tc_tsa[i] != IEEE_8021QAZ_TSA_ETS)
+			ets->tc_tx_bw[i] = 0;
+	}
+
 	return err;
 }
 
Signature · 1 line
2.53.0
author maintainer
#340 lore raw

PATCH 338/744 net/mlx5e: Reject unsupported CB Shaper TSA in ETS validation

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Alexei Lazar <[email protected]>

[ Upstream commit 9173e1d3c7c7d49a71eee813091f9e834ec7cee5 ]

Credit Based (CB) TSA is not supported by the mlx5 driver, so reject any configurations that specify it.

---
 drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c | 8 ++++++++
 1 file changed, 8 insertions(+)
drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c +8 -0
index 762f0a46c12097..00e706e1ede111 100644
@@ -324,6 +324,14 @@ static int mlx5e_dbcnl_validate_ets(struct net_device *netdev,
 		}
 	}
 
+	for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
+		if (ets->tc_tsa[i] == IEEE_8021QAZ_TSA_CB_SHAPER) {
+			netdev_err(netdev,
+				   "Failed to validate ETS: CB Shaper is not supported\n");
+			return -EOPNOTSUPP;
+		}
+	}
+
 	/* Validate Bandwidth Sum */
 	for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
 		if (ets->tc_tsa[i] == IEEE_8021QAZ_TSA_ETS) {
Signature · 1 line
2.53.0
author maintainer
#341 lore raw

PATCH 339/744 octeontx2-pf: tc: fix egress ratelimiting

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Hariprasad Kelam <[email protected]>

[ Upstream commit bb0d96ebe5f4d1acccf4dc36ca7f01f9a8fa1ba1 ]

The egress rate calculation computes an incorrect mantissa and exponent, causing up to ~50% deviation from the configured rate at lower speeds.

Rework the computation to follow the hardware rate formula:

	rate = 2 * (1 + mantissa/256) * 2^exp / (1 << div_exp)

Keep div_exp = 0 and derive exp and mantissa from half of the requested rate. Rates below 2 Mbps are floored to the smallest encodable step (exp = 0, mantissa = 0).

Fixes e638a83f167e ("octeontx2-pf: TC_MATCHALL egress ratelimiting offload")
---
 .../ethernet/marvell/octeontx2/nic/otx2_tc.c  | 29 ++++++++++---------
 1 file changed, 16 insertions(+), 13 deletions(-)
drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c +16 -13
index 40162b08014dd6..0b46ec29e64eaa 100644
@@ -30,6 +30,7 @@
 #define OTX2_UNSUPP_LSE_DEPTH		GENMASK(6, 4)
 
 #define MCAST_INVALID_GRP		(-1U)
+#define RATE_MANTISSA_BITS		8
 
 static void otx2_get_egress_burst_cfg(struct otx2_nic *nic, u32 burst,
 				      u32 *burst_exp, u32 *burst_mantissa)
@@ -66,28 +67,30 @@ static void otx2_get_egress_burst_cfg(struct otx2_nic *nic, u32 burst,
 static void otx2_get_egress_rate_cfg(u64 maxrate, u32 *exp,
 				     u32 *mantissa, u32 *div_exp)
 {
-	u64 tmp;
-
 	/* Rate calculation by hardware
 	 *
 	 * PIR_ADD = ((256 + mantissa) << exp) / 256
 	 * rate = (2 * PIR_ADD) / ( 1 << div_exp)
 	 * The resultant rate is in Mbps.
+	 *
+	 * Use div_exp = 0 and compute exp/mantissa for maxrate / 2; the
+	 * leading factor of two yields the full rate. Rates below 2 Mbps
+	 * are floored to the smallest step (exp = 0, mantissa = 0).
 	 */
 
-	/* 2Mbps to 100Gbps can be expressed with div_exp = 0.
-	 * Setting this to '0' will ease the calculation of
-	 * exponent and mantissa.
-	 */
 	*div_exp = 0;
-
 	if (maxrate) {
-		*exp = ilog2(maxrate) ? ilog2(maxrate) - 1 : 0;
-		tmp = maxrate - rounddown_pow_of_two(maxrate);
-		if (maxrate < MAX_RATE_MANTISSA)
-			*mantissa = tmp * 2;
-		else
-			*mantissa = tmp / (1ULL << (*exp - 7));
+		maxrate = maxrate / 2;
+		if (!maxrate) {
+			/* Rates below 2 Mbps map to the smallest step */
+			*exp = 0;
+			*mantissa = 0;
+		} else {
+			*exp = ilog2(maxrate);
+			/* Clear MSB and derive fractional bits */
+			maxrate &= ~BIT(*exp);
+			*mantissa = (maxrate << RATE_MANTISSA_BITS) >> *exp;
+		}
 	} else {
 		/* Instead of disabling rate limiting, set all values to max */
 		*exp = MAX_RATE_EXPONENT;
Signature · 1 line
2.53.0
author maintainer
#342 lore raw

PATCH 340/744 net: ipv6: fix dif and sdif mismatch in raw6_icmp_error

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Li RongQing <[email protected]>

[ Upstream commit 440e274da4d1b93c7df2cb0ce893c3009dd4db55 ]

In raw6_icmp_error(), raw_v6_match() is called with inet6_iif(skb) passed to both the 'dif' and 'sdif' arguments. This is a copy-paste or typo error, as the last argument should represent the secondary interface index (sdif).

This mismatch breaks ICMPv6 error handling for IPv6 raw sockets in VRF (Virtual Routing and Forwarding) environments. When a raw socket is bound to a VRF master device, raw_v6_match() fails to find a match because it is not given the correct sdif value, causing the socket to miss relevant ICMPv6 error notifications.

Fix this by properly passing inet6_sdif(skb) as the last argument to raw_v6_match().

Fixes 5108ab4bf446fa ("net: ipv6: add second dif to raw socket lookups")
---
 net/ipv6/raw.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
net/ipv6/raw.c +1 -1
index 3cc58698cbbd3a..b88d364e78aae9 100644
@@ -349,7 +349,7 @@ void raw6_icmp_error(struct sk_buff *skb, int nexthdr,
 		const struct ipv6hdr *ip6h = (const struct ipv6hdr *)skb->data;
 
 		if (!raw_v6_match(net, sk, nexthdr, &ip6h->saddr, &ip6h->daddr,
-				  inet6_iif(skb), inet6_iif(skb)))
+				  inet6_iif(skb), inet6_sdif(skb)))
 			continue;
 		rawv6_err(sk, skb, type, code, inner_offset, info);
 	}
Signature · 1 line
2.53.0
author maintainer
#343 lore raw

PATCH 341/744 ice: allow creating VFs when !CONFIG_ICE_SWITCHDEV

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Vincent Chen <[email protected]>

[ Upstream commit 99d0f42b0e5c57e4c02070a908aaff082881293a ]

Currently ice_eswitch_attach_vf() is called unconditionally in ice_start_vfs(), which causes VF creation to fail when CONFIG_ICE_SWITCHDEV is not defined.

Fix this by adding switchdev mode checks at the call sites before calling ice_eswitch_attach_vf(), consistent with how ice_eswitch_attach_sf() is already handled in ice_devlink_port_new(). This is similar to commit aacca7a83b97 ("ice: allow creating VFs for !CONFIG_NET_SWITCHDEV") which fixed the same issue for the previous ice_eswitch_configure() API.

---
 drivers/net/ethernet/intel/ice/ice_eswitch.c |  3 ---
 drivers/net/ethernet/intel/ice/ice_sriov.c   | 14 ++++++++------
 drivers/net/ethernet/intel/ice/ice_vf_lib.c  |  3 ++-
 3 files changed, 10 insertions(+), 10 deletions(-)
drivers/net/ethernet/intel/ice/ice_eswitch.c +0 -3
index c30e27bbfe6e25..b069e6c514fb12 100644
@@ -512,9 +512,6 @@ int ice_eswitch_attach_vf(struct ice_pf *pf, struct ice_vf *vf)
 	struct ice_repr *repr;
 	int err;
 
-	if (!ice_is_eswitch_mode_switchdev(pf))
-		return 0;
-
 	repr = ice_repr_create_vf(vf);
 	if (IS_ERR(repr))
 		return PTR_ERR(repr);
drivers/net/ethernet/intel/ice/ice_sriov.c +8 -6
index 7e00e091756ddc..e04de021559665 100644
@@ -484,12 +484,14 @@ static int ice_start_vfs(struct ice_pf *pf)
 			goto teardown;
 		}
 
-		retval = ice_eswitch_attach_vf(pf, vf);
-		if (retval) {
-			dev_err(ice_pf_to_dev(pf), "Failed to attach VF %d to eswitch, error %d",
-				vf->vf_id, retval);
-			ice_vf_vsi_release(vf);
-			goto teardown;
+		if (ice_is_eswitch_mode_switchdev(pf)) {
+			retval = ice_eswitch_attach_vf(pf, vf);
+			if (retval) {
+				dev_err(ice_pf_to_dev(pf), "Failed to attach VF %d to eswitch, error %d",
+					vf->vf_id, retval);
+				ice_vf_vsi_release(vf);
+				goto teardown;
+			}
 		}
 
 		set_bit(ICE_VF_STATE_INIT, vf->vf_states);
drivers/net/ethernet/intel/ice/ice_vf_lib.c +2 -1
index 27e4acb1620f0c..9052e71e9c99e3 100644
@@ -812,7 +812,8 @@ void ice_reset_all_vfs(struct ice_pf *pf)
 		}
 		ice_vf_post_vsi_rebuild(vf);
 
-		ice_eswitch_attach_vf(pf, vf);
+		if (ice_is_eswitch_mode_switchdev(pf))
+			ice_eswitch_attach_vf(pf, vf);
 
 		mutex_unlock(&vf->cfg_lock);
 	}
Signature · 1 line
2.53.0
author maintainer
#344 lore raw

PATCH 342/744 ice: fix LAG recipe to profile association

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Marcin Szycik <[email protected]>

[ Upstream commit d6da9b7d48599db078aea6144997a381f8d90d45 ]

ice_init_lag() associates recipes to profiles, assuming that Link Aggregation-related profiles will always have profile ID lower than 70 (ICE_PROFID_IPV6_GTPU_IPV6_TCP_INNER). This value seems arbitrary and might not always be valid for some versions of DDP package, i.e. LAG profiles may have profile ID greater than 70. This would lead to misconfigured switch and LAG not working properly.

Fix it by checking up to maximum profile ID.

---
 drivers/net/ethernet/intel/ice/ice_lag.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/ethernet/intel/ice/ice_lag.c +1 -1
index 310e8fe2925c7d..08a17ded0ad556 100644
@@ -2623,7 +2623,7 @@ int ice_init_lag(struct ice_pf *pf)
 		goto  free_lport_res;
 
 	/* associate recipes to profiles */
-	for (n = 0; n < ICE_PROFID_IPV6_GTPU_IPV6_TCP_INNER; n++) {
+	for (n = 0; n < ICE_MAX_NUM_PROFILES; n++) {
 		err = ice_aq_get_recipe_to_profile(&pf->hw, n,
 						   &recipe_bits, NULL);
 		if (err)
Signature · 1 line
2.53.0
author maintainer
#345 lore raw

PATCH 343/744 ice: prevent tstamp ring allocation for non-PF VSI types

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Paul Greenwalt <[email protected]>

[ Upstream commit 144539bbfd3cea1ab0fb6f5216d6004c1f4f029b ]

The pf->txtime_txqs bitmap tracks which Tx queues have ETF (Earliest TxTime First) offload enabled. This bitmap is indexed by queue number and is set by ice_offload_txtime(), which only operates on PF VSI queues.

However, ice_is_txtime_ena() does not check the VSI type before consulting the bitmap. When ETF offload is enabled on PF Tx queue 0, bit 0 is set in pf->txtime_txqs. During a subsequent PCI reset rebuild, the CTRL VSI's Tx queue 0 is reconfigured and ice_is_txtime_ena() is called for that ring. Since it only checks pf->txtime_txqs by queue index without distinguishing VSI type, it finds bit 0 set and returns true, matching the PF VSI's ETF queue, not the CTRL VSI's. This causes ice_vsi_cfg_txq() to spuriously allocate a tstamp_ring for the CTRL VSI ring.

Since CTRL VSI rings have no associated netdev, ice_clean_tx_ring() takes an early return at the !netdev check before reaching ice_free_tx_tstamp_ring(), leaking the allocation. Each PCI reset leaks one 64-byte tstamp_ring.

Fix this by restricting ice_is_txtime_ena() to return true only for PF VSI rings, since txtime_txqs is only meaningful for PF VSI queues.

---
 drivers/net/ethernet/intel/ice/ice.h | 3 +++
 1 file changed, 3 insertions(+)
drivers/net/ethernet/intel/ice/ice.h +3 -0
index 725b130dd3a2c6..f6671a653f21f2 100644
@@ -767,6 +767,9 @@ static inline bool ice_is_txtime_ena(const struct ice_tx_ring *ring)
 	struct ice_vsi *vsi = ring->vsi;
 	struct ice_pf *pf = vsi->back;
 
+	if (vsi->type != ICE_VSI_PF)
+		return false;
+
 	return test_bit(ring->q_index,  pf->txtime_txqs);
 }
 
Signature · 1 line
2.53.0
author maintainer
#346 lore raw

PATCH 344/744 idpf: fix max_vport related crash on allocation error during init

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Emil Tantilov <[email protected]>

[ Upstream commit 237f1f7653b8729169af11fae79f01b90d00b87e ]

Set adapter->max_vports only after successful allocation of vports, netdevs and vport_config buffers. This fixes possible crashes on reset or rmmod, following failed allocation on init

[  305.981402] idpf 0000:83:00.0: enabling device (0100 -> 0102)
[  305.994464] idpf 0000:83:00.0: Device HW Reset initiated
[  320.416872] BUG: kernel NULL pointer dereference, address: 0000000000000000
[  320.416918] #PF: supervisor read access in kernel mode
[  320.416942] #PF: error_code(0x0000) - not-present page
[  320.416963] PGD 2099657067 P4D 0
[  320.416983] Oops: Oops: 0000 [#1] SMP NOPTI
...
[  320.417093] RIP: 0010:idpf_remove+0x118/0x200 [idpf]
[  320.417130] Code: 8b bb 98 09 00 00 e8 17 0f 5b e5 48 8b bb e8 08 00 00 e8 0b 0f 5b e5 66 83 bb 28 06 00 00 00 48 8b bb 20 06 00 00 74 49 31 ed <48> 8b 04 ef 48 85 c0 74 2f 48 8b 78 20 e8 66 58 91 e5 48 8b 83 20
[  320.417183] RSP: 0018:ff7322212903fdb8 EFLAGS: 00010246
[  320.417205] RAX: 0000000000000000 RBX: ff4463de40300000 RCX: ff7322212903fd4c
[  320.417228] RDX: 0000000000000001 RSI: ffffffffa7f7d100 RDI: 0000000000000000
[  320.417250] RBP: 0000000000000000 R08: 0000000000000001 R09: 0000000000000000
[  320.417272] R10: 0000000000000001 R11: ff4463de3a638f58 R12: ff4463be89ac7000
[  320.417294] R13: ff4463be89ac7198 R14: ff4463be94fc7198 R15: ffffffffc0f10f20
[  320.417317] FS:  00007f963c0e6740(0000) GS:ff4463fdd65d8000(0000) knlGS:0000000000000000
[  320.417342] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[  320.417362] CR2: 0000000000000000 CR3: 00000020ba674002 CR4: 0000000000773ef0
[  320.417385] PKRU: 55555554
[  320.417398] Call Trace:
[  320.417412]  <TASK>
[  320.417429]  pci_device_remove+0x42/0xb0
[  320.417459]  device_release_driver_internal+0x1a9/0x210
[  320.417492]  driver_detach+0x4b/0x90
[  320.417516]  bus_remove_driver+0x70/0x100
[  320.417539]  pci_unregister_driver+0x2e/0xb0
[  320.417564]  __do_sys_delete_module.constprop.0+0x190/0x2f0
[  320.417592]  ? kmem_cache_free+0x31e/0x550
[  320.417619]  ? lockdep_hardirqs_on_prepare+0xde/0x190
[  320.417644]  ? do_syscall_64+0x38/0x6b0
[  320.417665]  do_syscall_64+0xc8/0x6b0
[  320.417683]  ? clear_bhb_loop+0x30/0x80
[  320.417706]  entry_SYSCALL_64_after_hwframe+0x76/0x7e
[  320.417727] RIP: 0033:0x7f963bb30beb
---
 drivers/net/ethernet/intel/idpf/idpf_virtchnl.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)
drivers/net/ethernet/intel/idpf/idpf_virtchnl.c +6 -1
index be66f9b2e101ca..dc5ad784f456f0 100644
@@ -3555,7 +3555,6 @@ int idpf_vc_core_init(struct idpf_adapter *adapter)
 
 	pci_sriov_set_totalvfs(adapter->pdev, idpf_get_max_vfs(adapter));
 	num_max_vports = idpf_get_max_vports(adapter);
-	adapter->max_vports = num_max_vports;
 	adapter->vports = kzalloc_objs(*adapter->vports, num_max_vports);
 	if (!adapter->vports)
 		return -ENOMEM;
@@ -3576,6 +3575,12 @@ int idpf_vc_core_init(struct idpf_adapter *adapter)
 		goto err_netdev_alloc;
 	}
 
+	/* Set max_vports only after vports, netdevs and vport_config buffers
+	 * are allocated to make sure max_vport bound loops don't end up
+	 * crashing, following allocation errors on init.
+	 */
+	adapter->max_vports = num_max_vports;
+
 	/* Start the mailbox task before requesting vectors. This will ensure
 	 * vector information response from mailbox is handled
 	 */
Signature · 1 line
2.53.0
author maintainer
#347 lore raw

PATCH 345/744 ipv6: Change allocation flags to match rcu_read_lock section requirements

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Nikola Z. Ivanov <[email protected]>

[ Upstream commit 313a123e1fca8827bb463db1f4bb211309764563 ]

Since the call to __ip6_del_rt_siblings has been converted under rcu read lock and it only has one call point
we should no longer block or yield.

Our stack trace from the syzbot reproducer looks as follows:

__ip6_del_rt_siblings
rtnl_notify (Here we pass gfp_any() -> GFP_KERNEL)
nlmsg_notify
nlmsg_multicast
nlmsg_multicast_filtered
netlink_broadcast_filtered (GFP_KERNEL passed from earlier)

netlink_broadcast_filtered can yield if GFP_KERNEL is passed, which we do not want to happen.

Fix this by changing the allocation flag of rtnl_notify.

Also change the flag passed to nlmsg_new. Even though it is not related to the syzbot generated bug it still falls under the same requirements.

---
 net/ipv6/route.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
net/ipv6/route.c +2 -2
index 88b46967bb67dc..d4edc133c045c4 100644
@@ -4020,7 +4020,7 @@ static int __ip6_del_rt_siblings(struct fib6_info *rt, struct fib6_config *cfg)
 		struct fib6_node *fn;
 
 		/* prefer to send a single notification with all hops */
-		skb = nlmsg_new(rt6_nlmsg_size(rt), gfp_any());
+		skb = nlmsg_new(rt6_nlmsg_size(rt), GFP_ATOMIC);
 		if (skb) {
 			u32 seq = info->nlh ? info->nlh->nlmsg_seq : 0;
 
@@ -4076,7 +4076,7 @@ static int __ip6_del_rt_siblings(struct fib6_info *rt, struct fib6_config *cfg)
 
 	if (skb) {
 		rtnl_notify(skb, net, info->portid, RTNLGRP_IPV6_ROUTE,
-			    info->nlh, gfp_any());
+			    info->nlh, GFP_ATOMIC);
 	}
 	return err;
 }
Signature · 1 line
2.53.0
author maintainer
#348 lore raw

PATCH 346/744 rds: tcp: unregister sysctl before tearing down listen socket

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Cen Zhang (Microsoft) <[email protected]>

[ Upstream commit 167e54c703ccd4fa028feb568b0d1002020cff86 ]

rds_tcp_exit_net() frees the per-netns RDS TCP listen socket via rds_tcp_kill_sock() before unregistering the per-netns sysctl table. Since rds_tcp_skbuf_handler() derives the netns from rtn->rds_tcp_listen_sock->sk, a concurrent sysctl write can race with netns teardown and dereference the freed socket/sk.

KASAN reports the race as:

  BUG: KASAN: slab-use-after-free in rds_tcp_skbuf_handler+0x2aa/0x2e0
  rds_tcp_skbuf_handler              net/rds/tcp.c:721
  proc_sys_call_handler              fs/proc/proc_sysctl.c
  vfs_write                          fs/read_write.c
  __x64_sys_pwrite64                 fs/read_write.c

Fix this by unregistering the RDS TCP sysctl table before calling rds_tcp_kill_sock(). unregister_net_sysctl_table() prevents new sysctl handlers from starting and waits for in-flight handlers to finish, so the listen socket can then be released safely. The fix was tested against the linked reproducer.

---
 net/rds/tcp.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
net/rds/tcp.c +2 -2
index d3acad60315ab0..76f441cbf50e64 100644
@@ -638,13 +638,13 @@ static void __net_exit rds_tcp_exit_net(struct net *net)
 {
 	struct rds_tcp_net *rtn = net_generic(net, rds_tcp_netid);
 
-	rds_tcp_kill_sock(net);
-
 	if (rtn->rds_tcp_sysctl)
 		unregister_net_sysctl_table(rtn->rds_tcp_sysctl);
 
 	if (net != &init_net)
 		kfree(rtn->ctl_table);
+
+	rds_tcp_kill_sock(net);
 }
 
 static struct pernet_operations rds_tcp_net_ops = {
Signature · 1 line
2.53.0
author maintainer
#349 lore raw

PATCH 347/744 ptp: netc: explicitly clear TMR_OFF during initialization

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Clark Wang <[email protected]>

[ Upstream commit c3f2fc231a39e29fe9f0adc14a3ecc3c1260d3c5 ]

The NETC timer does not support function level reset, so TMR_OFF_L/H registers are not cleared by pcie_flr(). If TMR_OFF was set to a non-zero value in a previous binding, it will persist across driver rebind and cause inaccurate PTP time.

There is also a hardware issue: after a warm reset or soft reset, TMR_OFF_L/H registers appear to be cleared to zero, but the timer clock domain internally retains the stale value. When the timer is re-enabled, TMR_CUR_TIME continues to track the old offset until TMR_OFF is written explicitly. This can cause incorrect PTP timestamps and even PTP clock synchronization failures.

Per the recommendation from the IP team, explicitly write 0 to TMR_OFF in netc_timer_init() to flush the internally cached value and ensure TMR_CUR_TIME follows the freshly initialized counter.

---
 drivers/ptp/ptp_netc.c | 1 +
 1 file changed, 1 insertion(+)
drivers/ptp/ptp_netc.c +1 -0
index 94e952ee69902e..5e381c354d746a 100644
@@ -779,6 +779,7 @@ static void netc_timer_init(struct netc_timer *priv)
 	netc_timer_wr(priv, NETC_TMR_FIPER_CTRL, fiper_ctrl);
 	netc_timer_wr(priv, NETC_TMR_ECTRL, NETC_TMR_DEFAULT_ETTF_THR);
 
+	netc_timer_offset_write(priv, 0);
 	ktime_get_real_ts64(&now);
 	ns = timespec64_to_ns(&now);
 	netc_timer_cnt_write(priv, ns);
Signature · 1 line
2.53.0
author maintainer
#350 lore raw

PATCH 348/744 mctp: check register_netdevice_notifier() error in mctp_device_init()

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Minhong He <[email protected]>

[ Upstream commit d9a33cadc70a94c1582f65e6042e81027cd200c6 ]

mctp_device_init() handles errors from rtnl_af_register() and rtnl_register_many(), but ignores the return value of register_netdevice_notifier(). If notifier registration fails, init can still return success while the module is only partially initialized.

Check the notifier registration error and fail module init early.

Fixes 583be982d934 ("mctp: Add device handling and netlink interface")
---
 net/mctp/device.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
net/mctp/device.c +3 -1
index 2c84df674669b2..822120e860c82a 100644
@@ -536,7 +536,9 @@ int __init mctp_device_init(void)
 {
 	int err;
 
-	register_netdevice_notifier(&mctp_dev_nb);
+	err = register_netdevice_notifier(&mctp_dev_nb);
+	if (err)
+		return err;
 
 	err = rtnl_af_register(&mctp_af_ops);
 	if (err)
Signature · 1 line
2.53.0
author maintainer
#351 lore raw

PATCH 349/744 net: airoha: fix ETS channel derivation in airoha_tc_setup_qdisc_ets()

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Lorenzo Bianconi <[email protected]>

[ Upstream commit 649ea07fc25a17aa51bff710baac1ab161022a7c ]

Derive the hardware QoS channel from opt->parent instead of opt->handle in airoha_tc_setup_qdisc_ets(). The ETS qdisc handle is either user-specified or auto-allocated by qdisc_alloc_handle() and bears no relation to the HTB leaf classid that identifies the hardware channel. HTB derives the channel from TC_H_MIN(opt->classid), and ETS is always attached as a child of an HTB leaf, so its opt->parent matches that classid. Using opt->handle instead can cause two ETS qdiscs on different HTB leaves to collide on the same hardware channel, corrupting scheduler configuration and stats.

---
 drivers/net/ethernet/airoha/airoha_eth.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)
drivers/net/ethernet/airoha/airoha_eth.c +1 -2
index 1e361a35ebd490..b03d1b2c820a5a 100644
@@ -2326,8 +2326,7 @@ static int airoha_tc_setup_qdisc_ets(struct net_device *dev,
 	if (opt->parent == TC_H_ROOT)
 		return -EINVAL;
 
-	channel = TC_H_MAJ(opt->handle) >> 16;
-	channel = channel % AIROHA_NUM_QOS_CHANNELS;
+	channel = TC_H_MIN(opt->parent) % AIROHA_NUM_QOS_CHANNELS;
 
 	switch (opt->command) {
 	case TC_ETS_REPLACE:
Signature · 1 line
2.53.0
author maintainer
#352 lore raw

PATCH 350/744 tipc: fix integer overflow in tipc_recvmsg() and tipc_recvstream()

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Cen Zhang (Microsoft) <[email protected]>

[ Upstream commit 47f42ff521b4eeb46e82f9a46a4783a99f7570d7 ]

In tipc_recvmsg(), the copy length is computed as:

  copy = min_t(int, dlen - offset, buflen);

buflen is size_t but min_t(int, ...) casts it to int. When buflen exceeds INT_MAX (e.g. 0xFFFFFFFF via io_uring provided buffers), it wraps negative, wins the comparison, and the negative copy length propagates to simple_copy_to_iter() where int-to-size_t promotion makes it SIZE_MAX, triggering a WARN_ON. tipc_recvstream() has the same pattern.

  Kernel panic - not syncing: kernel: panic_on_warn set ...
  RIP: 0010:simple_copy_to_iter+0x9e/0xd0 (net/core/datagram.c:521)
  Call Trace:
   __skb_datagram_iter+0x123/0x8b0 (net/core/datagram.c:402)
   skb_copy_datagram_iter+0x77/0x1a0 (net/core/datagram.c:534)
   tipc_recvmsg+0x3d7/0xe80 (net/tipc/socket.c:1934)
   io_recvmsg+0x47e/0xda0

Fix by changing min_t(int, ...) to min_t(size_t, ...) in both functions. The result is always <= (dlen - offset), which is bounded by TIPC maximum message size (0x1ffff bytes), so the implicit narrowing on assignment to int copy is always safe.

Fixes e9f8b10101c6 ("tipc: refactor function tipc_sk_recvmsg()")
Fixes ec8a09fbbeff ("tipc: refactor function tipc_sk_recv_stream()")
Reported-by [email protected]
---
 net/tipc/socket.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
net/tipc/socket.c +2 -2
index 490f30899b5a9a..14f3f2e5dba092 100644
@@ -1934,7 +1934,7 @@ static int tipc_recvmsg(struct socket *sock, struct msghdr *m,
 	if (likely(!err)) {
 		int offset = skb_cb->bytes_read;
 
-		copy = min_t(int, dlen - offset, buflen);
+		copy = min_t(size_t, dlen - offset, buflen);
 		rc = skb_copy_datagram_msg(skb, hlen + offset, m, copy);
 		if (unlikely(rc))
 			goto exit;
@@ -2066,7 +2066,7 @@ static int tipc_recvstream(struct socket *sock, struct msghdr *m,
 		/* Copy data if msg ok, otherwise return error/partial data */
 		if (likely(!err)) {
 			offset = skb_cb->bytes_read;
-			copy = min_t(int, dlen - offset, buflen - copied);
+			copy = min_t(size_t, dlen - offset, buflen - copied);
 			rc = skb_copy_datagram_msg(skb, hlen + offset, m, copy);
 			if (unlikely(rc))
 				break;
Signature · 1 line
2.53.0
author maintainer
#353 lore raw

PATCH 351/744 bnge/bng_re: fix ring ID widths

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Vikas Gupta <[email protected]>

[ Upstream commit b9e558976bb968162c35ddccdb076a77fc906993 ]

Firmware requires more than 16 bits to address TX ring IDs for its internal QP management. Widen the associated HSI ring ID fields to 32 bits. The values firmware assigns remain within 24 bits, bounded by the hardware doorbell XID field.

The fw_ring_id field belongs to bnge_ring_struct, a common struct shared by all ring types, so widening it to u32 applies uniformly across TX, RX, CP, and NQ rings but firmware assigns values within 16-bit range for all ring types except TX, which requires the wider field.

Note that, Thor Ultra hardware has not yet been deployed and no firmware has been released to field, so backward compatibility is not a concern.

---
 drivers/infiniband/hw/bng_re/bng_dev.c        |  6 +--
 drivers/net/ethernet/broadcom/bnge/bnge.h     |  1 +
 .../ethernet/broadcom/bnge/bnge_hwrm_lib.c    |  8 +--
 .../ethernet/broadcom/bnge/bnge_hwrm_lib.h    |  2 +-
 .../net/ethernet/broadcom/bnge/bnge_netdev.c  | 50 +++++++++----------
 .../net/ethernet/broadcom/bnge/bnge_netdev.h  |  4 +-
 .../net/ethernet/broadcom/bnge/bnge_rmem.h    |  2 +-
 include/linux/bnge/hsi.h                      |  7 ++-
 8 files changed, 39 insertions(+), 41 deletions(-)
drivers/infiniband/hw/bng_re/bng_dev.c +3 -3
index 71a7ca2196ad88..311c8bc931603a 100644
@@ -113,7 +113,7 @@ static void bng_re_fill_fw_msg(struct bnge_fw_msg *fw_msg, void *msg,
 }
 
 static int bng_re_net_ring_free(struct bng_re_dev *rdev,
-				u16 fw_ring_id, int type)
+				u32 fw_ring_id, int type)
 {
 	struct bnge_auxr_dev *aux_dev = rdev->aux_dev;
 	struct hwrm_ring_free_input req = {};
@@ -123,7 +123,7 @@ static int bng_re_net_ring_free(struct bng_re_dev *rdev,
 
 	bng_re_init_hwrm_hdr((void *)&req, HWRM_RING_FREE);
 	req.ring_type = type;
-	req.ring_id = cpu_to_le16(fw_ring_id);
+	req.ring_id = cpu_to_le32(fw_ring_id);
 	bng_re_fill_fw_msg(&fw_msg, (void *)&req, sizeof(req), (void *)&resp,
 			    sizeof(resp), BNGE_DFLT_HWRM_CMD_TIMEOUT);
 	rc = bnge_send_msg(aux_dev, &fw_msg);
@@ -161,7 +161,7 @@ static int bng_re_net_ring_alloc(struct bng_re_dev *rdev,
 			   sizeof(resp), BNGE_DFLT_HWRM_CMD_TIMEOUT);
 	rc = bnge_send_msg(aux_dev, &fw_msg);
 	if (!rc)
-		*fw_ring_id = le16_to_cpu(resp.ring_id);
+		*fw_ring_id = (u16)le32_to_cpu(resp.ring_id);
 
 	return rc;
 }
drivers/net/ethernet/broadcom/bnge/bnge.h +1 -0
index f21cff651fd44a..4479ccd071f532 100644
@@ -36,6 +36,7 @@ struct bnge_pf_info {
 };
 
 #define INVALID_HW_RING_ID      ((u16)-1)
+#define INVALID_HW_RING_ID_32BIT	(U32_MAX)
 
 enum {
 	BNGE_FW_CAP_SHORT_CMD				= BIT_ULL(0),
drivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c +4 -4
index 1c9cfec1b633fc..651c5e783516cc 100644
@@ -1283,7 +1283,7 @@ int bnge_hwrm_stat_ctx_alloc(struct bnge_net *bn)
 
 int hwrm_ring_free_send_msg(struct bnge_net *bn,
 			    struct bnge_ring_struct *ring,
-			    u32 ring_type, int cmpl_ring_id)
+			    u32 ring_type, u32 cmpl_ring_id)
 {
 	struct hwrm_ring_free_input *req;
 	struct bnge_dev *bd = bn->bd;
@@ -1295,7 +1295,7 @@ int hwrm_ring_free_send_msg(struct bnge_net *bn,
 
 	req->cmpl_ring = cpu_to_le16(cmpl_ring_id);
 	req->ring_type = ring_type;
-	req->ring_id = cpu_to_le16(ring->fw_ring_id);
+	req->ring_id = cpu_to_le32(ring->fw_ring_id);
 
 	bnge_hwrm_req_hold(bd, req);
 	rc = bnge_hwrm_req_send(bd, req);
@@ -1317,7 +1317,7 @@ int hwrm_ring_alloc_send_msg(struct bnge_net *bn,
 	struct hwrm_ring_alloc_output *resp;
 	struct hwrm_ring_alloc_input *req;
 	struct bnge_dev *bd = bn->bd;
-	u16 ring_id, flags = 0;
+	u32 ring_id, flags = 0;
 	int rc;
 
 	rc = bnge_hwrm_req_init(bd, req, HWRM_RING_ALLOC);
@@ -1401,7 +1401,7 @@ int hwrm_ring_alloc_send_msg(struct bnge_net *bn,
 
 	resp = bnge_hwrm_req_hold(bd, req);
 	rc = bnge_hwrm_req_send(bd, req);
-	ring_id = le16_to_cpu(resp->ring_id);
+	ring_id = le32_to_cpu(resp->ring_id);
 	bnge_hwrm_req_drop(bd, req);
 
 exit:
drivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.h +1 -1
index 3501de7a89b919..bf452e390d5bd1 100644
@@ -50,7 +50,7 @@ int bnge_hwrm_cfa_l2_set_rx_mask(struct bnge_dev *bd,
 void bnge_hwrm_stat_ctx_free(struct bnge_net *bn);
 int bnge_hwrm_stat_ctx_alloc(struct bnge_net *bn);
 int hwrm_ring_free_send_msg(struct bnge_net *bn, struct bnge_ring_struct *ring,
-			    u32 ring_type, int cmpl_ring_id);
+			    u32 ring_type, u32 cmpl_ring_id);
 int hwrm_ring_alloc_send_msg(struct bnge_net *bn,
 			     struct bnge_ring_struct *ring,
 			     u32 ring_type, u32 map_index);
drivers/net/ethernet/broadcom/bnge/bnge_netdev.c +24 -26

177 lines not on this page · raw message

drivers/net/ethernet/broadcom/bnge/bnge_netdev.h +2 -2
index f4636b5b0cf3f0..d177919c2e1170 100644
@@ -630,8 +630,8 @@ struct bnge_l2_filter {
 	refcount_t		refcnt;
 };
 
-u16 bnge_cp_ring_for_rx(struct bnge_rx_ring_info *rxr);
-u16 bnge_cp_ring_for_tx(struct bnge_tx_ring_info *txr);
+u32 bnge_cp_ring_for_rx(struct bnge_rx_ring_info *rxr);
+u32 bnge_cp_ring_for_tx(struct bnge_tx_ring_info *txr);
 void bnge_fill_hw_rss_tbl(struct bnge_net *bn, struct bnge_vnic_info *vnic);
 int bnge_alloc_rx_data(struct bnge_net *bn, struct bnge_rx_ring_info *rxr,
 		       u16 prod, gfp_t gfp);
drivers/net/ethernet/broadcom/bnge/bnge_rmem.h +1 -1
index 341c7f81ed092b..bb0c79a1ee60f7 100644
@@ -184,7 +184,7 @@ struct bnge_ctx_mem_info {
 struct bnge_ring_struct {
 	struct bnge_ring_mem_info	ring_mem;
 
-	u16			fw_ring_id;
+	u32			fw_ring_id;
 	union {
 		u16		grp_idx;
 		u16		map_idx; /* Used by NQs */
include/linux/bnge/hsi.h +3 -4
index 8ea13d5407eecd..1f7bd96415a527 100644
@@ -8317,8 +8317,7 @@ struct hwrm_ring_alloc_output {
 	__le16	req_type;
 	__le16	seq_id;
 	__le16	resp_len;
-	__le16	ring_id;
-	__le16	logical_ring_id;
+	__le32	ring_id;
 	u8	push_buffer_index;
 	#define RING_ALLOC_RESP_PUSH_BUFFER_INDEX_PING_BUFFER 0x0UL
 	#define RING_ALLOC_RESP_PUSH_BUFFER_INDEX_PONG_BUFFER 0x1UL
@@ -8345,10 +8344,10 @@ struct hwrm_ring_free_input {
 	u8	flags;
 	#define RING_FREE_REQ_FLAGS_VIRTIO_RING_VALID 0x1UL
 	#define RING_FREE_REQ_FLAGS_LAST             RING_FREE_REQ_FLAGS_VIRTIO_RING_VALID
-	__le16	ring_id;
+	__le16	unused_1;
 	__le32	prod_idx;
 	__le32	opaque;
-	__le32	unused_1;
+	__le32	ring_id;
 };
 
 /* hwrm_ring_free_output (size:128b/16B) */
Signature · 1 line
2.53.0
author maintainer
#354 lore raw

PATCH 352/744 net: drop_monitor: fix info leak in NET_DM_ATTR_PAYLOAD

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Yehyeong Lee <[email protected]>

[ Upstream commit 5e9c8baee0329fbefe7c67aea945e2a07f15e98b ]

net_dm_packet_report_fill() and net_dm_hw_packet_report_fill() open code the NET_DM_ATTR_PAYLOAD attribute to avoid zeroing the packet payload before overwriting it with skb_copy_bits().

skb_put() reserves nla_total_size(payload_len), i.e. the header plus the NLA_ALIGN() padding, but only payload_len bytes are copied in. When payload_len is not a multiple of 4 the 1-3 padding bytes are never initialized and are leaked to user space inside the netlink message.

KMSAN confirms the leak for the software path when the packet payload length is not 4-byte aligned:

  BUG: KMSAN: kernel-infoleak in _copy_to_iter
   _copy_to_iter
   __skb_datagram_iter
   skb_copy_datagram_iter
   netlink_recvmsg
   sock_recvmsg
   __sys_recvfrom
  Uninit was created at:
   kmem_cache_alloc_node_noprof
   __alloc_skb
   net_dm_packet_work
  Bytes 173-175 of 176 are uninitialized

Use __nla_reserve(), which sets up the attribute header and zeroes the padding, instead of open coding the attribute construction.

Fixes ca30707dee2b ("drop_monitor: Add packet alert mode")
Fixes 5e58109b1ea4 ("drop_monitor: Add support for packet alert mode for hardware drops")
---
 net/core/drop_monitor.c | 8 ++------
 1 file changed, 2 insertions(+), 6 deletions(-)
net/core/drop_monitor.c +2 -6
index f23cea9e1aafb1..afcd8ba411b6ac 100644
@@ -671,9 +671,7 @@ static int net_dm_packet_report_fill(struct sk_buff *msg, struct sk_buff *skb,
 	if (nla_put_u16(msg, NET_DM_ATTR_PROTO, be16_to_cpu(skb->protocol)))
 		goto nla_put_failure;
 
-	attr = skb_put(msg, nla_total_size(payload_len));
-	attr->nla_type = NET_DM_ATTR_PAYLOAD;
-	attr->nla_len = nla_attr_size(payload_len);
+	attr = __nla_reserve(msg, NET_DM_ATTR_PAYLOAD, payload_len);
 	if (skb_copy_bits(skb, 0, nla_data(attr), payload_len))
 		goto nla_put_failure;
 
@@ -831,9 +829,7 @@ static int net_dm_hw_packet_report_fill(struct sk_buff *msg,
 	if (nla_put_u16(msg, NET_DM_ATTR_PROTO, be16_to_cpu(skb->protocol)))
 		goto nla_put_failure;
 
-	attr = skb_put(msg, nla_total_size(payload_len));
-	attr->nla_type = NET_DM_ATTR_PAYLOAD;
-	attr->nla_len = nla_attr_size(payload_len);
+	attr = __nla_reserve(msg, NET_DM_ATTR_PAYLOAD, payload_len);
 	if (skb_copy_bits(skb, 0, nla_data(attr), payload_len))
 		goto nla_put_failure;
 
Signature · 1 line
2.53.0
author maintainer
#355 lore raw

PATCH 353/744 drop_monitor: fix size calculations for 64-bit attributes

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Eric Dumazet <[email protected]>

[ Upstream commit 7089f7ab99c89f443c92d8fcc585e63f2727f0b3 ]

net_dm_packet_report_fill() and net_dm_hw_packet_report_fill() use nla_put_u64_64bit() to append 64-bit attributes (NET_DM_ATTR_PC and NET_DM_ATTR_TIMESTAMP).

On 32-bit architectures without CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS, nla_put_u64_64bit() may append a 4-byte NET_DM_ATTR_PAD attribute for 64-bit alignment.

However, net_dm_packet_report_size() and net_dm_hw_packet_report_size() used nla_total_size(sizeof(u64)) instead of nla_total_size_64bit(sizeof(u64)), budgeting 12 bytes instead of up to 16 bytes.

This under-estimation of SKB size can lead to an skb_over_panic() when __nla_reserve() or skb_put() is subsequently called.

Fix this by using nla_total_size_64bit(sizeof(u64)) in both size calculations.

Fixes ca30707dee2b ("drop_monitor: Add packet alert mode")
Fixes 5e58109b1ea4 ("drop_monitor: Add support for packet alert mode for hardware drops")
---
 net/core/drop_monitor.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)
net/core/drop_monitor.c +3 -3
index afcd8ba411b6ac..61d6679b074eef 100644
@@ -566,13 +566,13 @@ static size_t net_dm_packet_report_size(size_t payload_len)
 	       /* NET_DM_ATTR_ORIGIN */
 	       nla_total_size(sizeof(u16)) +
 	       /* NET_DM_ATTR_PC */
-	       nla_total_size(sizeof(u64)) +
+	       nla_total_size_64bit(sizeof(u64)) +
 	       /* NET_DM_ATTR_SYMBOL */
 	       nla_total_size(NET_DM_MAX_SYMBOL_LEN + 1) +
 	       /* NET_DM_ATTR_IN_PORT */
 	       net_dm_in_port_size() +
 	       /* NET_DM_ATTR_TIMESTAMP */
-	       nla_total_size(sizeof(u64)) +
+	       nla_total_size_64bit(sizeof(u64)) +
 	       /* NET_DM_ATTR_ORIG_LEN */
 	       nla_total_size(sizeof(u32)) +
 	       /* NET_DM_ATTR_PROTO */
@@ -766,7 +766,7 @@ net_dm_hw_packet_report_size(size_t payload_len,
 	       /* NET_DM_ATTR_FLOW_ACTION_COOKIE */
 	       net_dm_flow_action_cookie_size(hw_metadata) +
 	       /* NET_DM_ATTR_TIMESTAMP */
-	       nla_total_size(sizeof(u64)) +
+	       nla_total_size_64bit(sizeof(u64)) +
 	       /* NET_DM_ATTR_ORIG_LEN */
 	       nla_total_size(sizeof(u32)) +
 	       /* NET_DM_ATTR_PROTO */
Signature · 1 line
2.53.0
author maintainer
#356 lore raw

PATCH 354/744 drop_monitor: perform u64_stats updates under IRQ-disabled section

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Eric Dumazet <[email protected]>

[ Upstream commit fd098a23bf8fda7eae48db9b06e7c34fc4d228fa ]

In net_dm_packet_trace_kfree_skb_hit() and net_dm_hw_trap_packet_probe(), u64_stats_update_begin() / u64_stats_inc() / u64_stats_update_end() were called after spin_unlock_irqrestore(&...drop_queue.lock, flags), when local IRQs had already been re-enabled.

Tracepoint probes can execute in IRQ or softirq context. On 32-bit architectures, u64_stats_update_begin() disables preemption but not interrupts, relying on seqcount writes. If a nested interrupt occurs on the same CPU during the 64-bit stats update, the reentrant seqcount update can corrupt the seqcount state or stats value.

Fix this by performing the 64-bit per-CPU stats update before releasing drop_queue.lock via spin_unlock_irqrestore(), ensuring local interrupts remain disabled during the u64_stats update.

Fixes e9feb58020f9 ("drop_monitor: Expose tail drop counter")
Fixes 5e58109b1ea4 ("drop_monitor: Add support for packet alert mode for hardware drops")
---
 net/core/drop_monitor.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
net/core/drop_monitor.c +2 -2
index 61d6679b074eef..5df3e18ebaf74a 100644
@@ -530,10 +530,10 @@ static void net_dm_packet_trace_kfree_skb_hit(void *ignore,
 	return;
 
 unlock_free:
-	spin_unlock_irqrestore(&data->drop_queue.lock, flags);
 	u64_stats_update_begin(&data->stats.syncp);
 	u64_stats_inc(&data->stats.dropped);
 	u64_stats_update_end(&data->stats.syncp);
+	spin_unlock_irqrestore(&data->drop_queue.lock, flags);
 	consume_skb(nskb);
 }
 
@@ -997,10 +997,10 @@ net_dm_hw_trap_packet_probe(void *ignore, const struct devlink *devlink,
 	return;
 
 unlock_free:
-	spin_unlock_irqrestore(&hw_data->drop_queue.lock, flags);
 	u64_stats_update_begin(&hw_data->stats.syncp);
 	u64_stats_inc(&hw_data->stats.dropped);
 	u64_stats_update_end(&hw_data->stats.syncp);
+	spin_unlock_irqrestore(&hw_data->drop_queue.lock, flags);
 	net_dm_hw_metadata_free(n_hw_metadata);
 free:
 	consume_skb(nskb);
Signature · 1 line
2.53.0
author maintainer
#357 lore raw

PATCH 355/744 pidfs: make pidfs_ino_lock static

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Mateusz Guzik <[email protected]>

[ Upstream commit c1d04c1bce98f9dd984a9c6657278a7761854c9c ]

Fixes 87caaeef7995 ("pidfs: implement ino allocation without the pidmap lock")
---
 fs/pidfs.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
fs/pidfs.c +1 -1
index 143d0aec16af10..33874c891d285e 100644
@@ -107,7 +107,7 @@ struct pidfs_attr {
 
 #if BITS_PER_LONG == 32
 
-DEFINE_SPINLOCK(pidfs_ino_lock);
+static DEFINE_SPINLOCK(pidfs_ino_lock);
 static u64 pidfs_ino_nr = 1;
 
 static inline unsigned long pidfs_ino(u64 ino)
Signature · 1 line
2.53.0
author maintainer
#358 lore raw

PATCH 356/744 LoongArch: BPF: Fix memory leak in bpf_jit_free()

In reply to Greg Kroah-Hartman

7.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Pu Lehui <[email protected]>

[ Upstream commit 47e20d4b3da97ef3881d1e55e43545c22424f3fc ]

When bpf_int_jit_compile() is called for subprograms, it returns early during the first pass (!prog->is_func || extra_pass is false), keeping ctx->offset alive for the subsequent extra pass.

If JIT compilation fails for a later subprogram, the BPF core aborts and calls bpf_jit_free() to clean up the first subprogram. However, bpf_jit_free() fails to free jit_data->ctx.offset, which causes a memory leak of the JIT context offsets array.

So fix this by adding the missing kvfree(jit_data->ctx.offset) in bpf_jit_free().

Fixes 4ab17e762b34 ("LoongArch: BPF: Use BPF prog pack allocator")
---
 arch/loongarch/net/bpf_jit.c | 1 +
 1 file changed, 1 insertion(+)
arch/loongarch/net/bpf_jit.c +1 -0
index 22c03c02dae6ed..4933869b2c11fb 100644
@@ -2347,6 +2347,7 @@ void bpf_jit_free(struct bpf_prog *prog)
 		 */
 		if (jit_data) {
 			bpf_jit_binary_pack_finalize(jit_data->ro_header, jit_data->header);
+			kvfree(jit_data->ctx.offset);
 			kfree(jit_data);
 		}
 		hdr = bpf_jit_binary_pack_hdr(prog);
Signature · 1 line
2.53.0