Skip to contentSkip to conversation

7.2.4-rc1 review

PATCH In review Greg Kroah-Hartman 15 participants 731 messages Sep 4 – Sep 18 Page 6 of 6

Subsystems (2)
lore
Share & tools
lore
author maintainer
#633 lore raw

PATCH 627/713 net: cap advertised IP tunnel headroom

In reply to Greg Kroah-Hartman

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

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

From: Zhiling Zou <[email protected]>

commit 6b222adeb9340306e2ff97127c76117abb9b3df8 upstream.

IP tunnel devices derive their advertised needed_headroom from lower output devices. A stack of user-created devices can make the derived value larger than the 16-bit skb header offsets can represent. Once IP output reserves it, skb head expansion can wrap those offsets.

The runtime transmit path already caps a growing needed_headroom at 512. Apply the same cap when tunnel configuration publishes needed_headroom derived from a lower output device.

Capping the advertised value is safe: IP tunnel transmit still expands the skb when a packet needs more headroom. A nonsensical stacked configuration can therefore incur an extra reallocation, but it cannot publish an unbounded reservation to upper layers.

---
 include/net/ip_tunnels.h |   11 +++++++++--
 net/ipv4/ip_tunnel.c     |    2 +-
 net/ipv6/ip6_gre.c       |    7 +++++--
 net/ipv6/ip6_tunnel.c    |    7 +++++--
 net/ipv6/sit.c           |    2 +-
 5 files changed, 21 insertions(+), 8 deletions(-)
include/net/ip_tunnels.h +9 -2
@@ -629,8 +629,7 @@ struct metadata_dst *iptunnel_metadata_r
 int skb_tunnel_check_pmtu(struct sk_buff *skb, struct dst_entry *encap_dst,
 			  int headroom, bool reply);
 
-static inline void ip_tunnel_adj_headroom(struct net_device *dev,
-					  unsigned int headroom)
+static inline unsigned int ip_tunnel_limit_headroom(unsigned int headroom)
 {
 	/* we must cap headroom to some upperlimit, else pskb_expand_head
 	 * will overflow header offsets in skb_headers_offset_update().
@@ -640,6 +639,14 @@ static inline void ip_tunnel_adj_headroo
 	if (headroom > max_allowed)
 		headroom = max_allowed;
 
+	return headroom;
+}
+
+static inline void ip_tunnel_adj_headroom(struct net_device *dev,
+					  unsigned int headroom)
+{
+	headroom = ip_tunnel_limit_headroom(headroom);
+
 	if (headroom > READ_ONCE(dev->needed_headroom))
 		WRITE_ONCE(dev->needed_headroom, headroom);
 }
net/ipv4/ip_tunnel.c +1 -1
@@ -317,7 +317,7 @@ static int ip_tunnel_bind_dev(struct net
 		mtu = min(tdev->mtu, IP_MAX_MTU);
 	}
 
-	dev->needed_headroom = t_hlen + hlen;
+	dev->needed_headroom = ip_tunnel_limit_headroom(t_hlen + hlen);
 	mtu -= t_hlen + (dev->type == ARPHRD_ETHER ? dev->hard_header_len : 0);
 
 	if (mtu < IPV4_MIN_MTU)
net/ipv6/ip6_gre.c +5 -2
@@ -1137,8 +1137,11 @@ static void ip6gre_tnl_link_config_route
 			return;
 
 		if (rt->dst.dev) {
-			dev->needed_headroom = rt->dst.dev->hard_header_len +
-					       t_hlen;
+			unsigned int headroom;
+
+			headroom = rt->dst.dev->hard_header_len + t_hlen;
+			headroom = ip_tunnel_limit_headroom(headroom);
+			dev->needed_headroom = headroom;
 
 			if (set_mtu) {
 				int mtu = rt->dst.dev->mtu - t_hlen;
net/ipv6/ip6_tunnel.c +5 -2
@@ -1514,8 +1514,11 @@ static void ip6_tnl_link_config(struct i
 			tdev = __dev_get_by_index(t->net, p->link);
 
 		if (tdev) {
-			dev->needed_headroom = tdev->hard_header_len +
-				tdev->needed_headroom + t_hlen;
+			unsigned int headroom;
+
+			headroom = tdev->hard_header_len + tdev->needed_headroom;
+			headroom += t_hlen;
+			dev->needed_headroom = ip_tunnel_limit_headroom(headroom);
 			mtu = min_t(unsigned int, tdev->mtu, IP6_MAX_MTU);
 
 			mtu = mtu - t_hlen;
net/ipv6/sit.c +1 -1
@@ -1131,7 +1131,7 @@ static void ipip6_tunnel_bind_dev(struct
 		WRITE_ONCE(dev->mtu, mtu);
 		hlen = tdev->hard_header_len + tdev->needed_headroom;
 	}
-	dev->needed_headroom = t_hlen + hlen;
+	dev->needed_headroom = ip_tunnel_limit_headroom(t_hlen + hlen);
 }
 
 static void ipip6_tunnel_update(struct ip_tunnel *t,
author maintainer
#634 lore raw

PATCH 628/713 net: fix spurious TX timeout after dev_activate()

In reply to Greg Kroah-Hartman

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

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

From: Breno Leitao <[email protected]>

commit 82aeed2400786bd3f79d88cb8b8f42e6127e5923 upstream.

While debugging another issue today, I found out that my TX queue is reported as stopped for 4294907392 ms (49.7 days), on a machine that had been up for four minutes.

    bnxt_en 0002:01:00.0 eth0: NETDEV WATCHDOG: CPU: 28: transmit queue 23 timed out 4294907392 ms

4294907392 is not an elapsed time. It is the value of jiffies at that
moment: INITIAL_JIFFIES is 4294667296, which leaves jiffies 59 seconds short of wrapping.

dev_activate() runs transition_one_qdisc() over every TX queue, which resets trans_start to 0, and then stamps only queue 0 through netif_trans_update().

Stamp jiffies instead. A queue stopped across dev_activate() now gets a full watchdog_timeo of grace, and is still reported if it is stopped that long.

---
 net/sched/sch_generic.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
net/sched/sch_generic.c +1 -1
@@ -1278,7 +1278,7 @@ static void transition_one_qdisc(struct
 
 	rcu_assign_pointer(dev_queue->qdisc, new_qdisc);
 	if (need_watchdog_p) {
-		WRITE_ONCE(dev_queue->trans_start, 0);
+		WRITE_ONCE(dev_queue->trans_start, jiffies);
 		*need_watchdog_p = 1;
 	}
 }
author maintainer
#635 lore raw

PATCH 629/713 net: skbuff: dont touch shared zerocopy state in skb_tx_error()

In reply to Greg Kroah-Hartman

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

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

From: Norbert Szetei <[email protected]>

commit f66bdb1cc0fcd227a062378f8be0b5873aa5600a upstream.

skb_tx_error() completes the zerocopy uarg and clears SKBFL_ALL_ZEROCOPY, and skb_zcopy_downgrade_managed() clears SKBFL_MANAGED_FRAG_REFS. Both live in skb_shinfo(), which every clone shares, while the caller only owns the reference it is about to drop. Through a clone it tells the producer its pages are free and drops SKBFL_SHARED_FRAG for an skb that is still in flight.

Open vSwitch reaches this with a non-last OVS_ACTION_ATTR_RECIRC:
clone_execute() sends a skb_clone() into ovs_dp_process_packet() while do_execute_actions() keeps forwarding the original, and skb_clone() does not privatise the frags here -- skb_orphan_frags() returns early on SKBFL_DONT_ORPHAN. A flow miss on the clone then strips the marker from the packet still being forwarded, and a later local ESP delivery decrypts in place over frags it does not own privately.

Skip it for a cloned skb. Nothing is lost: skb_release_data() clears the zerocopy state once the last reference to the shared data goes.

---
 net/core/skbuff.c |    5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)
net/core/skbuff.c +4 -1
@@ -1417,10 +1417,13 @@ EXPORT_SYMBOL(skb_dump);
  *
  *	Report xmit error if a device callback is tracking this skb.
  *	skb must be freed afterwards.
+ *
+ *	Does nothing for a cloned skb: the zerocopy state lives in
+ *	skb_shinfo(), which the clones share.
  */
 void skb_tx_error(struct sk_buff *skb)
 {
-	if (skb) {
+	if (skb && !skb_cloned(skb)) {
 		skb_zcopy_downgrade_managed(skb);
 		skb_zcopy_clear(skb, true);
 	}
author maintainer
#636 lore raw

PATCH 630/713 seg6: reset IP6CB after IPv6 decapsulation

In reply to Greg Kroah-Hartman

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

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

From: Zhiling Zou <[email protected]>

commit f967455fb2a5a2079b9eb5823e9ccf359174bf9f upstream.

decap_and_validate() pulls the outer SRv6 headers and makes the inner packet the skb network header. The IPv6 control block still contains values collected while parsing the outer packet, including nhoff and extension-header flags.

End.DX6 and End.DT6 route the inner IPv6 packet directly to the IPv6 input path. An unprivileged user can reach End.DT6 from a user and net namespace by installing a local SID and injecting an outer packet with Hop-by-Hop and Destination Options headers followed by an SRH and a minimal inner IPv6 packet.

The outer extension headers leave a large nhoff in IP6CB. After decapsulation, ip6_protocol_deliver_rcu() uses that stale offset on the inner packet and reads beyond the skb head. KASAN reports:

  BUG: KASAN: slab-out-of-bounds in ip6_protocol_deliver_rcu
  ip6_protocol_deliver_rcu+0x1118/0x1450
  ip6_input_finish+0x11b/0x240
  seg6_local_input_core+0xed/0x2e0
  lwtunnel_input+0x1e9/0x4e0
  ipv6_rthdr_rcv+0x525f/0x6c50
  ip6_protocol_deliver_rcu+0xcb7/0x1450

Before clearing IP6CB for an inner IPv6 packet, save its incoming interface index and L3 slave state. Restore both after the clear and set nhoff to the inner IPv6 base-header nexthdr field.

Use IP6CB(skb)->iif rather than skb->skb_iif because VRF processing can replace skb_iif with the L3 master while IP6CB keeps the receiving interface. Preserve IP6SKB_L3SLAVE for the same reason.

Fixes d7a669dd2f8b ("ipv6: sr: add helper functions for seg6local")
---
 net/ipv6/seg6_local.c |    9 +++++++++
 1 file changed, 9 insertions(+)
net/ipv6/seg6_local.c +9 -0
@@ -261,6 +261,15 @@ static bool decap_and_validate(struct sk
 
 		memset(IPCB(skb), 0, sizeof(*IPCB(skb)));
 		IPCB(skb)->iif = iif;
+	} else if (proto == IPPROTO_IPV6) {
+		bool l3slave = ipv6_l3mdev_skb(IP6CB(skb)->flags);
+		int iif = IP6CB(skb)->iif;
+
+		memset(IP6CB(skb), 0, sizeof(*IP6CB(skb)));
+		IP6CB(skb)->iif = iif;
+		IP6CB(skb)->nhoff = offsetof(struct ipv6hdr, nexthdr);
+		if (l3slave)
+			IP6CB(skb)->flags |= IP6SKB_L3SLAVE;
 	}
 
 	return true;
author maintainer
#637 lore raw

PATCH 631/713 hwrng: stm32 - Fix runtime PM cleanup on registration failure

In reply to Greg Kroah-Hartman

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

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

From: Can Peng <[email protected]>

commit 1163a476a568f6c0f852d469c8e4c5a5f805adac upstream.

stm32_rng_probe() enables autosuspend and runtime PM before registering the hwrng. If devm_hwrng_register() fails, probe returns with runtime PM left enabled and autosuspend still selected.

The remove callback also only disables runtime PM and does not undo pm_runtime_use_autosuspend().

Use devm_pm_runtime_enable() so runtime PM is unwound automatically on probe failure and driver detach. Since the managed cleanup also disables runtime PM,drop the remove callback.

Fixes c6a97c42e399 ("hwrng: stm32 - add support for STM32 HW RNG")
---
 drivers/char/hw_random/stm32-rng.c |   10 +++-------
 1 file changed, 3 insertions(+), 7 deletions(-)
drivers/char/hw_random/stm32-rng.c +3 -7
@@ -368,11 +368,6 @@ static int stm32_rng_init(struct hwrng *
 	return 0;
 }
 
-static void stm32_rng_remove(struct platform_device *ofdev)
-{
-	pm_runtime_disable(&ofdev->dev);
-}
-
 static int __maybe_unused stm32_rng_runtime_suspend(struct device *dev)
 {
 	struct stm32_rng_private *priv = dev_get_drvdata(dev);
@@ -590,7 +585,9 @@ static int stm32_rng_probe(struct platfo
 
 	pm_runtime_set_autosuspend_delay(dev, 100);
 	pm_runtime_use_autosuspend(dev);
-	pm_runtime_enable(dev);
+	ret = devm_pm_runtime_enable(dev);
+	if (ret)
+		return ret;
 
 	return devm_hwrng_register(dev, &priv->rng);
 }
@@ -602,7 +599,6 @@ static struct platform_driver stm32_rng_
 		.of_match_table = stm32_rng_match,
 	},
 	.probe = stm32_rng_probe,
-	.remove = stm32_rng_remove,
 };
 
 module_platform_driver(stm32_rng_driver);
author maintainer
#638 lore raw

PATCH 632/713 mfd: cgbc: Fix teardown ordering in cgbc_remove()

In reply to Greg Kroah-Hartman

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

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

From: Thomas Richard <[email protected]>

commit 2970c2db8ab3d97ea6250c14ca49b1e1731115ab upstream.

Release Board Controller session once children are removed by the core.

---
 drivers/mfd/cgbc-core.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/mfd/cgbc-core.c +2 -2
@@ -364,9 +364,9 @@ static void cgbc_remove(struct platform_
 {
 	struct cgbc_device_data *cgbc = platform_get_drvdata(pdev);
 
-	cgbc_session_release(cgbc);
-
 	mfd_remove_devices(&pdev->dev);
+
+	cgbc_session_release(cgbc);
 }
 
 static struct platform_driver cgbc_driver = {
author maintainer
#639 lore raw

PATCH 633/713 mfd: qnap-mcu: keep the reply buffer alive past a command timeout

In reply to Greg Kroah-Hartman

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

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

From: Ali Ahmet Memis <[email protected]>

commit 47504742cea7878ebd1bf1491bbed923df6b90b1 upstream.

qnap_mcu_exec() publishes an on-stack buffer to the receive path:

	unsigned char rx[QNAP_MCU_RX_BUFFER_SIZE];
	...
	reply->data = rx;
	reply->length = length;

and qnap_mcu_receive_buf() writes into it from the serdev receive path, which runs out of flush_to_ldisc() and is not serialized against qnap_mcu_exec() at all. bus_lock cannot cover it, because qnap_mcu_exec() holds that mutex across wait_for_completion_timeout().

On a timeout qnap_mcu_exec() returns with reply->data still pointing at its own frame. A reply that arrives late, or an unsolicited message from the MCU, is then written into a stack frame that has been left, corrupting whatever runs next on that stack. The same applies when qnap_mcu_write() fails, since that path returns without touching the reply state either.

Move the receive buffer into struct qnap_mcu. It is 37 bytes and the structure is devm_kzalloc()ed, so it lives as long as the driver, and a late write lands in memory that is still valid and is reinitialized by the next command. bus_lock keeps commands from sharing it.

This deliberately does not clear reply->data or reply->length on the timeout path. Doing so races with qnap_mcu_receive_buf(), which reads both after its

	if (!reply->length)
		return size;

check: clearing reply->data gives a NULL dereference, and clearing reply->length alone removes the reply->received == reply->length exit condition, so the copy loop runs until the uart chunk is consumed and overruns the buffer. Leaving both set keeps the write bounded by reply->length, which qnap_mcu_exec() has already checked against sizeof(mcu->rx).

---
 drivers/mfd/qnap-mcu.c |   13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)
drivers/mfd/qnap-mcu.c +7 -6
@@ -56,6 +56,7 @@ struct qnap_mcu_reply {
  * @reply:	Reply data structure
  * @variant:	Device variant specific information
  * @version:	MCU firmware version
+ * @rx:		Receive buffer the reply is assembled in
  */
 struct qnap_mcu {
 	struct serdev_device *serdev;
@@ -63,6 +64,7 @@ struct qnap_mcu {
 	struct qnap_mcu_reply reply;
 	const struct qnap_mcu_variant *variant;
 	u8 version[QNAP_MCU_VERSION_LEN];
+	u8 rx[QNAP_MCU_RX_BUFFER_SIZE];
 };
 
 /*
@@ -214,19 +216,18 @@ int qnap_mcu_exec(struct qnap_mcu *mcu,
 		  const u8 *cmd_data, size_t cmd_data_size,
 		  u8 *reply_data, size_t reply_data_size)
 {
-	unsigned char rx[QNAP_MCU_RX_BUFFER_SIZE];
 	size_t length = reply_data_size + QNAP_MCU_CHECKSUM_SIZE;
 	struct qnap_mcu_reply *reply = &mcu->reply;
 	int ret = 0;
 
-	if (length > sizeof(rx)) {
+	if (length > sizeof(mcu->rx)) {
 		dev_err(&mcu->serdev->dev, "expected data too big for receive buffer");
 		return -EINVAL;
 	}
 
 	guard(mutex)(&mcu->bus_lock);
 
-	reply->data = rx;
+	reply->data = mcu->rx;
 	reply->length = length;
 	reply->received = 0;
 	reinit_completion(&reply->done);
@@ -242,15 +243,15 @@ int qnap_mcu_exec(struct qnap_mcu *mcu,
 		return -ETIMEDOUT;
 	}
 
-	if (!qnap_mcu_verify_checksum(rx, reply->received)) {
+	if (!qnap_mcu_verify_checksum(mcu->rx, reply->received)) {
 		dev_err(&mcu->serdev->dev, "Invalid Checksum received from controller\n");
 		return -EPROTO;
 	}
 
-	if (qnap_mcu_reply_is_any_error(mcu, rx, reply->received))
+	if (qnap_mcu_reply_is_any_error(mcu, mcu->rx, reply->received))
 		return -EPROTO;
 
-	memcpy(reply_data, rx, reply_data_size);
+	memcpy(reply_data, mcu->rx, reply_data_size);
 
 	return 0;
 }
author maintainer
#640 lore raw

PATCH 634/713 mfd: sm501: Fix potential memory leaks during remove

In reply to Greg Kroah-Hartman

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

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

From: Abdun Nihaal <[email protected]>

commit 83feedd9d83c0c5199f98c72df0a6196b4aefb4d upstream.

The memory allocated for struct sm501_devdata in sm501_pci_probe() and sm501_plat_probe() is not freed by the corresponding remove functions sm501_pci_remove() and sm501_plat_remove(). Fix that by adding a call to kfree().

---
 drivers/mfd/sm501.c |    2 ++
 1 file changed, 2 insertions(+)
drivers/mfd/sm501.c +2 -0
@@ -1627,6 +1627,7 @@ static void sm501_pci_remove(struct pci_
 	release_mem_region(sm->io_res->start, 0x100);
 
 	pci_disable_device(dev);
+	kfree(sm);
 }
 
 static void sm501_plat_remove(struct platform_device *dev)
@@ -1637,6 +1638,7 @@ static void sm501_plat_remove(struct pla
 	iounmap(sm->regs);
 
 	release_mem_region(sm->io_res->start, 0x100);
+	kfree(sm);
 }
 
 static const struct pci_device_id sm501_pci_tbl[] = {
author maintainer
#641 lore raw

PATCH 635/713 ALSA: 6fire: bound the MIDI event length from the device

In reply to Greg Kroah-Hartman

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

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

From: Baul Lee <[email protected]>

commit a478893b59e36cfe7d77a76b352f2db55502e879 upstream.

usb6fire_comm_receiver_handler() forwards a MIDI event using a length byte the device supplies, with no bound and no check that the transfer delivered that many bytes:

	if (!urb->status) {
		if (rt->receiver_buffer[0] == 0x10) /* midi in event */
			if (midi_rt)
				midi_rt->in_received(midi_rt,
						rt->receiver_buffer + 2,
						rt->receiver_buffer[1]);
	}

receiver_buffer is a 64-byte kzalloc() buffer (COMM_RECEIVER_BUFSIZE), so only 62 bytes follow the two-byte header. receiver_buffer[1] is a u8 the device chooses, so a device that answers with 0x10 and a length of 0xFF makes snd_rawmidi_receive() read 255 bytes starting two bytes into a 64-byte object. The bytes past the buffer are handed to userspace through the rawmidi read path.

urb->actual_length is not consulted either, so a short transfer leaves both the type byte and the length byte at their previous values and the handler acts on stale data.

The receiver URB is submitted from usb6fire_comm_init() at probe, so the read happens on plug with no user action; forwarding to userspace also needs a MIDI input substream open, since usb6fire_midi_in_received() only calls snd_rawmidi_receive() when rt->in is set.

KASAN on 7.2.0-rc5 (arm64), single packet from an emulated device:

  BUG: KASAN: slab-out-of-bounds in snd_rawmidi_receive
  Read of size 255 at addr ffff000009f64682 by task bash/183
   __asan_memcpy
   snd_rawmidi_receive
   usb6fire_midi_in_received [snd_usb_6fire]
   usb6fire_comm_receiver_handler [snd_usb_6fire]
  Allocated by task 11:
   usb6fire_comm_init [snd_usb_6fire]
   usb6fire_chip_probe [snd_usb_6fire]
  The buggy address is located 2 bytes inside of
   allocated 64-byte region [ffff000009f64680, ffff000009f646c0)

Reject the event when the length exceeds the bytes that follow the header, and require the transfer to have delivered the header plus that many bytes. The receiver URB is submitted with a 64-byte transfer_buffer_length, so a genuine device cannot deliver an event longer than those 62 bytes and nothing valid is dropped.

Discovered by XBOW, triaged by Baul Lee <[email protected]>

---
 sound/usb/6fire/comm.c |    9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)
sound/usb/6fire/comm.c +6 -3
@@ -36,11 +36,14 @@ static void usb6fire_comm_receiver_handl
 	struct midi_runtime *midi_rt = rt->chip->midi;
 
 	if (!urb->status) {
-		if (rt->receiver_buffer[0] == 0x10) /* midi in event */
+		u8 len = rt->receiver_buffer[1];
+
+		if (rt->receiver_buffer[0] == 0x10 && /* midi in event */
+		    len <= COMM_RECEIVER_BUFSIZE - 2 &&
+		    urb->actual_length >= len + 2)
 			if (midi_rt)
 				midi_rt->in_received(midi_rt,
-						rt->receiver_buffer + 2,
-						rt->receiver_buffer[1]);
+						rt->receiver_buffer + 2, len);
 	}
 
 	if (!rt->chip->shutdown) {
author maintainer
#642 lore raw

PATCH 636/713 ALSA: aloop: Check card index validity at probe

In reply to Greg Kroah-Hartman

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

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

From: Takashi Iwai <[email protected]>

commit 819b106a9fd2ef3fd8abf898b9a8e4524eca8f48 upstream.

aloop driver blindly trusts that the given devptr->id value is within the proper card index range at probe. It's OK for the devices the driver itself creates at the module probe time, but if the device is bound manually via sysfs interface, this could be -1 as "none", and this leads to OOB access for index[] and other parameters.

Add a sanity check for the card index and warn/correct it if it's a value out of the range.

---
 sound/drivers/aloop.c |    6 ++++++
 1 file changed, 6 insertions(+)
sound/drivers/aloop.c +6 -0
@@ -1802,6 +1802,12 @@ static int loopback_probe(struct platfor
 	int dev = devptr->id;
 	int err;
 
+	if (dev < 0 || dev >= SNDRV_CARDS) {
+		dev_warn(&devptr->dev,
+			 "Invalid card index %d, using default 0\n", dev);
+		dev = 0;
+	}
+
 	err = snd_devm_card_new(&devptr->dev, index[dev], id[dev], THIS_MODULE,
 				sizeof(struct loopback), &card);
 	if (err < 0)
author maintainer
#643 lore raw

PATCH 637/713 ALSA: bcd2000: clear the URB pointers on disconnect

In reply to Greg Kroah-Hartman

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

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

From: Baul Lee <[email protected]>

commit 459d3a64766f5ca2f1886daeaf24582831a5f5ab upstream.

bcd2000_free_usb_related_resources() frees both URBs and leaves the pointers behind:

	usb_kill_urb(bcd2k->midi_out_urb);
	usb_kill_urb(bcd2k->midi_in_urb);

	usb_free_urb(bcd2k->midi_out_urb);
	usb_free_urb(bcd2k->midi_in_urb);

The rawmidi device outlives that call. A substream that is still open when the device is unplugged reaches bcd2000_midi_send() from the trigger path on close. That function writes to the freed URB and then hands it to the USB core:

	bcd2k->midi_out_urb->transfer_buffer_length = BUFSIZE;
	...
	ret = usb_submit_urb(bcd2k->midi_out_urb, GFP_ATOMIC);

usb_kill_urb() does not stop a later submission either, so a submit that races the disconnect can requeue the URB after it has been reaped. midi_in_urb is exposed the same way: bcd2000_input_complete() resubmits it from the completion handler.

KASAN on 7.2.0-rc5 (arm64):

  BUG: KASAN: slab-use-after-free in bcd2000_midi_send [snd_bcd2000]
  Write of size 4 at addr ffff00001827d388 by task bpoc/168
   __asan_store4
   bcd2000_midi_send [snd_bcd2000]
   bcd2000_midi_output_trigger [snd_bcd2000]
   snd_rawmidi_kernel_write1
   close_substream.part.0
  Freed by task 168:
   usb_free_urb
   bcd2000_disconnect [snd_bcd2000]

  BUG: KASAN: slab-use-after-free in usb_submit_urb
  Read of size 8 at addr ffff00001827d3b8 by task bpoc/168

Clear both pointers after freeing and test them on the paths that can still run. Poison the URBs before freeing them: usb_poison_urb() waits for a running completion handler and rejects any later submission, so after it returns the input path is quiesced and only the rawmidi trigger path can still reach bcd2000_midi_send(). No unpoison is needed; the URBs are freed on the next line.

Discovered by XBOW, triaged by Baul Lee <[email protected]>

---
 sound/usb/bcd2000/bcd2000.c |   11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)
sound/usb/bcd2000/bcd2000.c +8 -3
@@ -134,6 +134,9 @@ static void bcd2000_midi_send(struct bcd
 	if (!midi_out_substream)
 		return;
 
+	if (!bcd2k->midi_out_urb)
+		return;
+
 	/* copy command prefix bytes */
 	memcpy(bcd2k->midi_out_buf, device_cmd_prefix,
 		sizeof(device_cmd_prefix));
@@ -178,7 +181,7 @@ static int bcd2000_midi_output_close(str
 {
 	struct bcd2000 *bcd2k = substream->rmidi->private_data;
 
-	if (bcd2k->midi_out_active) {
+	if (bcd2k->midi_out_active && bcd2k->midi_out_urb) {
 		usb_kill_urb(bcd2k->midi_out_urb);
 		bcd2k->midi_out_active = 0;
 	}
@@ -348,11 +351,13 @@ static int bcd2000_init_midi(struct bcd2
 static void bcd2000_free_usb_related_resources(struct bcd2000 *bcd2k,
 						struct usb_interface *interface)
 {
-	usb_kill_urb(bcd2k->midi_out_urb);
-	usb_kill_urb(bcd2k->midi_in_urb);
+	usb_poison_urb(bcd2k->midi_out_urb);
+	usb_poison_urb(bcd2k->midi_in_urb);
 
 	usb_free_urb(bcd2k->midi_out_urb);
 	usb_free_urb(bcd2k->midi_in_urb);
+	bcd2k->midi_out_urb = NULL;
+	bcd2k->midi_in_urb = NULL;
 
 	if (bcd2k->intf) {
 		usb_set_intfdata(bcd2k->intf, NULL);
author maintainer
#644 lore raw

PATCH 638/713 ALSA: FCP: do not copy out an uninitialised init response

In reply to Greg Kroah-Hartman

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

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

From: Baul Lee <[email protected]>

commit 4335e387786479889e6db691fe06d345e52ea536 upstream.

fcp_ioctl_init() allocates its response buffer with kmalloc() and copies the whole buffer back to userspace:

	buf_size = init.step0_resp_size + init.step2_resp_size;

	void *resp __free(kfree) =
		kmalloc(buf_size, GFP_KERNEL);
	...
	if (copy_to_user(arg->resp, resp, buf_size))
		return -EFAULT;

Nothing clears the buffer, and the only writer of its leading step0_resp_size bytes is the step-0 control transfer:

	err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0),
		FCP_USB_REQ_STEP0,
		USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
		0, private->bInterfaceNumber,
		step0_resp, private->step0_resp_size);
	if (err < 0)
		return err;

usb_fill_control_urb() does not set URB_SHORT_NOT_OK, so a short or zero-length data stage completes with status 0 and snd_usb_ctl_msg() returns a small actual_length. The only check is err < 0, so a short transfer is accepted as success.

snd_usb_ctl_msg() copies the full size back unconditionally:

	buf = kmemdup(data, size, GFP_KERNEL);
	...
	memcpy(data, buf, size);

Bytes the device never wrote are therefore restored into resp unchanged and copied to userspace. step0_resp_size and step2_resp_size are each validated only to 1..255, so the caller also picks the slab cache, from kmalloc-8 up to kmalloc-512.

On 7.2.0-rc5 (arm64), device answering step 0 with a zero-length data stage, s0 = s2 = 255:

  # init_on_alloc off, no spray
  step0 window [0,255): nonzero=94/255
  000: 00 80 60 06 00 00 ff ff 18 00 00 00 57 01 ea 01
  010: 08 78 22 13 00 00 ff ff a8 c4 5f 80 00 80 ff ff
  # same kernel, kmalloc-512 pre-seeded with an 8-byte tag
  step0 window [0,255): nonzero=219/255  tagbytes=232
  # identical run, init_on_alloc=1
  step0 window [0,255): nonzero=0/255  tagbytes=0
  # all three runs
  step2 window [255,510): device words matched=62/62

a8 c4 5f 80 00 80 ff ff is the little-endian kernel text address ffff8000805fc4a8. The step-2 window is unaffected, so the disclosure is exactly the step-0 region.

Zero the buffer, and require the step-0 transfer to deliver the full step0_resp_size bytes so a short data stage is reported as an error.

Discovered by XBOW, triaged by Baul Lee <[email protected]>

---
 sound/usb/fcp.c |    4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
sound/usb/fcp.c +3 -1
@@ -494,7 +494,7 @@ static int fcp_ioctl_init(struct usb_mix
 	buf_size = init.step0_resp_size + init.step2_resp_size;
 
 	void *resp __free(kfree) =
-		kmalloc(buf_size, GFP_KERNEL);
+		kzalloc(buf_size, GFP_KERNEL);
 	if (!resp)
 		return -ENOMEM;
 
@@ -1033,6 +1033,8 @@ static int fcp_init(struct usb_mixer_int
 		step0_resp, private->step0_resp_size);
 	if (err < 0)
 		return err;
+	if (err != private->step0_resp_size)
+		return -EIO;
 
 	err = fcp_init_notify(mixer);
 	if (err < 0)
author maintainer
#645 lore raw

PATCH 639/713 ALSA: hda/ext: preserve PPLCCTL bits when clearing reset

In reply to Greg Kroah-Hartman

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

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

From: Xu Rao <[email protected]>

commit 36aa66de481d29edd63cbad9b5c4dc18c340fdf6 upstream.

snd_hdac_ext_stream_reset() polls PPLCCTL for STRST by masking the register value with AZX_PPLCCTL_STRST:

	val = readl(...) & AZX_PPLCCTL_STRST;

The same masked value is then used when clearing STRST. Since val contains no bits other than STRST, clearing STRST from it always produces zero. The subsequent writel() therefore writes zero to the entire PPLCCTL register instead of clearing only the reset bit.

PPLCCTL contains other stream control fields, including the stream tag in AZX_PPLCCTL_STRM_MASK. Those fields must not be modified as a side effect of clearing stream reset.

Use snd_hdac_updatel() to clear STRST, matching the existing set-reset path and preserving all unrelated PPLCCTL bits.

Fixes df203a4e46f4 ("ALSA: hdac_ext: add extended stream capabilities")
---
 sound/hda/core/ext/stream.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
sound/hda/core/ext/stream.c +2 -2
@@ -210,8 +210,8 @@ void snd_hdac_ext_stream_reset(struct hd
 			break;
 		udelay(3);
 	} while (--timeout);
-	val &= ~AZX_PPLCCTL_STRST;
-	writel(val, hext_stream->pplc_addr + AZX_REG_PPLCCTL);
+	snd_hdac_updatel(hext_stream->pplc_addr, AZX_REG_PPLCCTL,
+			 AZX_PPLCCTL_STRST, 0);
 	udelay(3);
 
 	timeout = 50;
author maintainer
#646 lore raw

PATCH 640/713 ALSA: mpu401: Check card index validity at probe

In reply to Greg Kroah-Hartman

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

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

From: Takashi Iwai <[email protected]>

commit f7dcecb92ed192ff5fcf842918fb1aaea84b5bdd upstream.

mpu401 driver blindly trusts that the given devptr->id value is within the proper card index range at probe. It's OK for the devices the driver itself creates at the module probe time, but if the device is bound manually via sysfs interface, this could be -1 as "none", and this leads to OOB access for index[] and other parameters.

Add a sanity check for the card index and warn/correct it if it's a value out of the range.

---
 sound/drivers/mpu401/mpu401.c |    6 ++++++
 1 file changed, 6 insertions(+)
sound/drivers/mpu401/mpu401.c +6 -0
@@ -89,6 +89,12 @@ static int snd_mpu401_probe(struct platf
 	int err;
 	struct snd_card *card;
 
+	if (dev < 0 || dev >= SNDRV_CARDS) {
+		dev_warn(&devptr->dev,
+			 "Invalid card index %d, using default 0\n", dev);
+		dev = 0;
+	}
+
 	if (port[dev] == SNDRV_AUTO_PORT) {
 		dev_err(&devptr->dev, "specify port\n");
 		return -EINVAL;
author maintainer
#647 lore raw

PATCH 641/713 ALSA: mts64: Check card index validity at probe

In reply to Greg Kroah-Hartman

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

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

From: Takashi Iwai <[email protected]>

commit d18a260720f86a5f8b5fcfefc4ba2e9dd01c10f8 upstream.

Although mts64 driver has a check of the given devptr->id value, it doesn't check for a negative id, which is often given as "none" or such value when bound via sysfs. This may lead to OOB access for index[] and other parameters.

Add a sanity check for the card index and warn/correct it if it's a value out of the range.

---
 sound/drivers/mts64.c |    6 ++++++
 1 file changed, 6 insertions(+)
sound/drivers/mts64.c +6 -0
@@ -900,6 +900,12 @@ static int snd_mts64_probe(struct platfo
 	p = platform_get_drvdata(pdev);
 	platform_set_drvdata(pdev, NULL);
 
+	if (dev < 0) {
+		dev_warn(&pdev->dev,
+			 "Invalid card index %d, using default 0\n", dev);
+		dev = 0;
+	}
+
 	if (dev >= SNDRV_CARDS)
 		return -ENODEV;
 	if (!enable[dev]) 
author maintainer
#648 lore raw

PATCH 642/713 ALSA: pcxhr: initialize mutexes before requesting threaded IRQ

In reply to Greg Kroah-Hartman

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

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

From: Runyu Xiao <[email protected]>

commit 6c97817e20598e5473094e0e38d1f51f1cf4dfff upstream.

pcxhr_probe() requests pcxhr_threaded_irq() before initializing mgr->lock, even though the threaded handler takes that mutex.

Initialize the manager locks before request_threaded_irq() so an early interrupt cannot run against uninitialized mutex state during probe.

---
 sound/pci/pcxhr/pcxhr.c |   14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)
sound/pci/pcxhr/pcxhr.c +7 -7
@@ -1535,6 +1535,13 @@ static int pcxhr_probe(struct pci_dev *p
 	mgr->pci = pci;
 	mgr->irq = -1;
 
+	/* ISR lock  */
+	mutex_init(&mgr->lock);
+	mutex_init(&mgr->msg_lock);
+
+	/* init setup mutex*/
+	mutex_init(&mgr->setup_mutex);
+
 	if (request_threaded_irq(pci->irq, pcxhr_interrupt,
 				 pcxhr_threaded_irq, IRQF_SHARED,
 				 KBUILD_MODNAME, mgr)) {
@@ -1548,13 +1555,6 @@ static int pcxhr_probe(struct pci_dev *p
 		 "Digigram at 0x%lx & 0x%lx, 0x%lx irq %i",
 		 mgr->port[0], mgr->port[1], mgr->port[2], mgr->irq);
 
-	/* ISR lock  */
-	mutex_init(&mgr->lock);
-	mutex_init(&mgr->msg_lock);
-
-	/* init setup mutex*/
-	mutex_init(&mgr->setup_mutex);
-
 	mgr->prmh = kmalloc(sizeof(*mgr->prmh) +
 			    sizeof(u32) * (PCXHR_SIZE_MAX_LONG_STATUS -
 					   PCXHR_SIZE_MAX_STATUS),
author maintainer
#649 lore raw

PATCH 643/713 ALSA: portman2x4: Check card index validity at probe

In reply to Greg Kroah-Hartman

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

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

From: Takashi Iwai <[email protected]>

commit 3690ef20469d5959378260e2752f2314a2572913 upstream.

Although portman2x4 driver has a check of the given devptr->id value, it doesn't check for a negative id, which is often given as "none" or such value when bound via sysfs. This may lead to OOB access for index[] and other parameters.

Add a sanity check for the card index and warn/correct it if it's a value out of the range.

---
 sound/drivers/portman2x4.c |    6 ++++++
 1 file changed, 6 insertions(+)
sound/drivers/portman2x4.c +6 -0
@@ -697,6 +697,12 @@ static int snd_portman_probe(struct plat
 	p = platform_get_drvdata(pdev);
 	platform_set_drvdata(pdev, NULL);
 
+	if (dev < 0) {
+		dev_warn(&pdev->dev,
+			 "Invalid card index %d, using default 0\n", dev);
+		dev = 0;
+	}
+
 	if (dev >= SNDRV_CARDS)
 		return -ENODEV;
 	if (!enable[dev]) 
author maintainer
#650 lore raw

PATCH 644/713 ALSA: serial-u16550: Check card index validity at probe

In reply to Greg Kroah-Hartman

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

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

From: Takashi Iwai <[email protected]>

commit e0fb960b227fcdebe22e4f26c9486d60943c0424 upstream.

serial-u16550 driver blindly trusts that the given devptr->id value is within the proper card index range at probe. It's OK for the devices the driver itself creates at the module probe time, but if the device is bound manually via sysfs interface, this could be -1 as "none", and this leads to OOB access for index[] and other parameters.

Add a sanity check for the card index and warn/correct it if it's a value out of the range.

---
 sound/drivers/serial-u16550.c |    6 ++++++
 1 file changed, 6 insertions(+)
sound/drivers/serial-u16550.c +6 -0
@@ -846,6 +846,12 @@ static int snd_serial_probe(struct platf
 	int err;
 	int dev = devptr->id;
 
+	if (dev < 0 || dev >= SNDRV_CARDS) {
+		dev_warn(&devptr->dev,
+			 "Invalid card index %d, using default 0\n", dev);
+		dev = 0;
+	}
+
 	switch (adaptor[dev]) {
 	case SNDRV_SERIAL_SOUNDCANVAS:
 		ins[dev] = 1;
author maintainer
#651 lore raw

PATCH 645/713 ALSA: virmidi: Check card index validity at probe

In reply to Greg Kroah-Hartman

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

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

From: Takashi Iwai <[email protected]>

commit b65d5182ecd6b7a24a83d980a0d06e809ef876c5 upstream.

virmidi driver blindly trusts that the given devptr->id value is within the proper card index range at probe. It's OK for the devices the driver itself creates at the module probe time, but if the device is bound manually via sysfs interface, this could be -1 as "none", and this leads to OOB access for index[] and other parameters.

Add a sanity check for the card index and warn/correct it if it's a value out of the range.

---
 sound/drivers/virmidi.c |    6 ++++++
 1 file changed, 6 insertions(+)
sound/drivers/virmidi.c +6 -0
@@ -75,6 +75,12 @@ static int snd_virmidi_probe(struct plat
 	int idx, err;
 	int dev = devptr->id;
 
+	if (dev < 0 || dev >= SNDRV_CARDS) {
+		dev_warn(&devptr->dev,
+			 "Invalid card index %d, using default 0\n", dev);
+		dev = 0;
+	}
+
 	err = snd_devm_card_new(&devptr->dev, index[dev], id[dev], THIS_MODULE,
 				sizeof(struct snd_card_virmidi), &card);
 	if (err < 0)
author maintainer
#652 lore raw

PATCH 646/713 ALSA: hda/realtek: Add quirk for TongFang XxAF5xxx

In reply to Greg Kroah-Hartman

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

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

From: Eckhart Mohr <[email protected]>

commit e72d5659a2606056a0c34af212b46a3275a55bbf upstream.

Fix microphone detection on built in headphone jack for some devices

---
 sound/hda/codecs/realtek/alc269.c |    1 +
 1 file changed, 1 insertion(+)
sound/hda/codecs/realtek/alc269.c +1 -0
@@ -8055,6 +8055,7 @@ static const struct hda_quirk alc269_fix
 	SND_PCI_QUIRK(0x1d05, 0x3031, "TongFang X6AR55xU", ALC2XX_FIXUP_HEADSET_MIC),
 	SND_PCI_QUIRK(0x1d05, 0x3033, "TongFang X6SP45xU", ALC2XX_FIXUP_HEADSET_MIC),
 	SND_PCI_QUIRK(0x1d05, 0x3034, "TongFang X6KK45xU", ALC2XX_FIXUP_HEADSET_MIC),
+	SND_PCI_QUIRK(0x1d05, 0x30ba, "TongFang XxAF5xxx", ALC2XX_FIXUP_HEADSET_MIC),
 	SND_PCI_QUIRK(0x1d17, 0x3288, "Haier Boyue G42", ALC269VC_FIXUP_ACER_VCOPPERBOX_PINS),
 	SND_PCI_QUIRK(0x1d72, 0x1602, "RedmiBook", ALC255_FIXUP_XIAOMI_HEADSET_MIC),
 	SND_PCI_QUIRK(0x1d72, 0x1701, "XiaomiNotebook Pro", ALC298_FIXUP_DELL1_MIC_NO_PRESENCE),
author maintainer
#653 lore raw

PATCH 647/713 ALSA: hda/realtek: Enable headset mic on F+ FLAPTOP r

In reply to Greg Kroah-Hartman

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

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

From: Ajrat Makhmutov <[email protected]>

commit 7097666b993b37f4e47982026b703b2379a364f8 upstream.

The BIOS of the F+ FLAPTOP r laptop (Realtek ALC897, SSID 1e63:6d9a) declares only pin 0x1b, the headphone output of the 3.5 mm combo jack. Every other external pin is left at 0x411111f0, so the headset mic pin 0x19 is never parsed and no headset mic input exists. The pin is wired on this board - retasking it makes the headset mic record.

Reuse ALC897_FIXUP_HP_HSMIC_VERB, which already sets the pin config this machine needs: 0x19 as a headset mic without its own presence detect. Only 0x1b reports jack presence here, so a mic pin with presence detect would leave the driver in auto-mic mode waiting for an event that never arrives.

Without the quirk the generic parser retasks the lone headphone pin as an input instead. That surfaces as a "Headphone Mic" input which records only the internal mic bleed, so the headset mic appears present but dead.

Tested on ALT Linux, kernel 6.12, by recording a CTIA headset mic on the combo jack with the internal mic as a reference.

ALSA info before the patch:
https://alsa-project.org/db/?f=18363eddea933baee100c9bf461d0e5cf74c8de2

ALSA info after the patch:
https://alsa-project.org/db/?f=48ae2cd7aaf1eb0f24639ce83cd38cfd93b25f76

---
 sound/hda/codecs/realtek/alc662.c |    1 +
 1 file changed, 1 insertion(+)
sound/hda/codecs/realtek/alc662.c +1 -0
@@ -852,6 +852,7 @@ static const struct hda_quirk alc662_fix
 	SND_PCI_QUIRK(0x1b35, 0x1234, "CZC ET26", ALC662_FIXUP_CZC_ET26),
 	SND_PCI_QUIRK(0x1b35, 0x2206, "CZC P10T", ALC662_FIXUP_CZC_P10T),
 	SND_PCI_QUIRK(0x1c6c, 0x1239, "Compaq N14JP6-V2", ALC897_FIXUP_HP_HSMIC_VERB),
+	SND_PCI_QUIRK(0x1e63, 0x6d9a, "F+ FLAPTOP r", ALC897_FIXUP_HP_HSMIC_VERB),
 
 #if 0
 	/* Below is a quirk table taken from the old code.
author maintainer
#654 lore raw

PATCH 648/713 ALSA: hda/realtek: Enable micmute LED on HP EliteBook 6 G1a p/n: AD3Q9ET#UUG

In reply to Greg Kroah-Hartman

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

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

From: Denis Batishchev <[email protected]>

commit e7da28b820d12927de30abf554c727a319f80359 upstream.

The HP EliteBook 6 G1a (SSID 103c:8e0d) uses a Realtek ALC236 codec. Without a quirk no fixup is selected and the mic-mute LED stays off. It needs the same ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF quirk as the already-supported 14" variant (SSID 103c:8dfb), so add it.

---
 sound/hda/codecs/realtek/alc269.c |    1 +
 1 file changed, 1 insertion(+)
sound/hda/codecs/realtek/alc269.c +1 -0
@@ -7442,6 +7442,7 @@ static const struct hda_quirk alc269_fix
 	SND_PCI_QUIRK(0x103c, 0x8dfc, "HP EliteBook 645 G12", ALC236_FIXUP_HP_GPIO_LED),
 	SND_PCI_QUIRK(0x103c, 0x8dfd, "HP EliteBook 6 G1a 16", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
 	SND_PCI_QUIRK(0x103c, 0x8dfe, "HP EliteBook 665 G12", ALC236_FIXUP_HP_GPIO_LED),
+	SND_PCI_QUIRK(0x103c, 0x8e0d, "HP EliteBook 6 G1a 14 (AD3Q9ET#UUG)", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
 	SND_PCI_QUIRK(0x103c, 0x8e11, "HP Trekker", ALC287_FIXUP_CS35L41_I2C_2),
 	SND_PCI_QUIRK(0x103c, 0x8e12, "HP Trekker", ALC287_FIXUP_CS35L41_I2C_2),
 	SND_PCI_QUIRK(0x103c, 0x8e13, "HP Trekker", ALC287_FIXUP_CS35L41_I2C_2),
author maintainer
#655 lore raw

PATCH 649/713 ALSA: hda/realtek: Fix Lenovo Yoga Slim 7 14AKP10 quirk ordering

In reply to Greg Kroah-Hartman

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

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

From: Zhang Heng <[email protected]>

commit 75dc2eda659f6be4a370734f11baf25df8a9fd80 upstream.

The Yoga Slim 7 14AKP10 has a PCI SSID of 17aa:38b4 but a codec SSID of 17aa:391a. The current quirk table contains a PCI quirk for 17aa:38b4 (for the Legion Slim 7 16IRH8) which matches first, so the codec-specific quirk for 17aa:391a is never applied.

This results in the wrong fixup being used (CS35L41_I2C_2 instead of the correct bass speaker fixup), leaving the internal speakers misconfigured or silent.

Remove the 17aa:391a entry from its PCI-SSID-sorted position and add it as an HDA_CODEC_QUIRK directly before the 17aa:38b4 entry, because it must match on the codec subsystem ID rather than the PCI SSID and it has to win over the colliding PCI quirk for the Legion Slim 7 16IRH8. A comment is added to explain the out-of-order placement, following the same style already used for the 17aa:38bb and 17aa:38f9 codec-SSID overrides.

With this change, the correct ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN is applied, restoring speaker output and auto-mute functionality.

The original quirk added in commit e6c888202297 ("ALSA: hda/realtek: Add quirk for Lenovo Yoga Slim 7 14AKP10") matched on the PCI SSID 17aa:391a, but this model actually exposes PCI SSID 17aa:38b4 (shared with the Legion Slim 7 16IRH8), so that quirk never matched and the bass speaker remained silent. Fix it by matching on the codec SSID and placing the entry before the colliding 17aa:38b4 PCI quirk.

---
 sound/hda/codecs/realtek/alc269.c |    5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)
sound/hda/codecs/realtek/alc269.c +4 -1
@@ -7950,6 +7950,10 @@ static const struct hda_quirk alc269_fix
 	SND_PCI_QUIRK(0x17aa, 0x38a8, "Y780P AMD VECO dual", ALC287_FIXUP_TAS2781_I2C),
 	SND_PCI_QUIRK(0x17aa, 0x38a9, "Thinkbook 16P", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD),
 	SND_PCI_QUIRK(0x17aa, 0x38ab, "Thinkbook 16P", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD),
+	/* Lenovo Yoga Slim 7 14AKP10 shares PCI SSID 17aa:38b4 with Legion Slim 7
+	 * 16IRH8; use codec SSID to distinguish them
+	 */
+	HDA_CODEC_QUIRK(0x17aa, 0x391a, "Lenovo Yoga Slim 7 14AKP10", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN),
 	SND_PCI_QUIRK(0x17aa, 0x38b4, "Legion Slim 7 16IRH8", ALC287_FIXUP_CS35L41_I2C_2),
 	HDA_CODEC_QUIRK(0x17aa, 0x391c, "Lenovo Yoga 7 2-in-1 14AKP10", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN),
 	HDA_CODEC_QUIRK(0x17aa, 0x391d, "Lenovo Yoga 7 2-in-1 16AKP10", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN),
@@ -7990,7 +7994,6 @@ static const struct hda_quirk alc269_fix
 	SND_PCI_QUIRK(0x17aa, 0x3911, "Lenovo Yoga Pro 7 14IAH10", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN),
 	SND_PCI_QUIRK(0x17aa, 0x3912, "Lenovo Xiaoxin 14 GT", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN),
 	SND_PCI_QUIRK(0x17aa, 0x3913, "Lenovo 145", ALC236_FIXUP_LENOVO_INV_DMIC),
-	SND_PCI_QUIRK(0x17aa, 0x391a, "Lenovo Yoga Slim 7 14AKP10", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN),
 	SND_PCI_QUIRK(0x17aa, 0x391f, "Yoga S990-16 pro Quad YC Quad", ALC287_FIXUP_TXNW2781_I2C),
 	SND_PCI_QUIRK(0x17aa, 0x3920, "Yoga S990-16 pro Quad VECO Quad", ALC287_FIXUP_TXNW2781_I2C),
 	SND_PCI_QUIRK(0x17aa, 0x3929, "Thinkbook 13x Gen 5", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD),
author maintainer
#656 lore raw

PATCH 650/713 rust: num: reject Bounded::shr overshifts at build time

In reply to Greg Kroah-Hartman

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

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

From: Eliot Courtney <[email protected]>

[ Upstream commit 223aa25aee82e188ddf043a8703b16e5fdfc37d8 ]

Make shr reject shifts of at least the type's bit width at build time, instead of panicking or masking the shift amount at runtime.

[ This implies we can break the type invariant, which in turn means
we can trigger UB via Deref, e.g.:

rust_kernel: panicked at rust/kernel/num/bounded.rs:528:22:
unsafe precondition(s) violated: hint::unreachable_unchecked must never be reached

- Miguel ]

---
 rust/kernel/num/bounded.rs | 1 +
 1 file changed, 1 insertion(+)
rust/kernel/num/bounded.rs +1 -0
index 304ef04d86cd9..6d766c22e61b5 100644
@@ -495,6 +495,7 @@ pub fn cast<U>(self) -> Bounded<U, N>
     /// assert_eq!(v_shifted.get(), 0xff);
     /// ```
     pub fn shr<const SHIFT: u32, const RES: u32>(self) -> Bounded<T, RES> {
+        const_assert!(SHIFT < T::BITS);
         const { assert!(RES + SHIFT >= N) }
 
         // SAFETY: We shift the value right by `SHIFT`, reducing the number of bits needed to
Signature · 1 line
2.53.0
author maintainer
#657 lore raw

PATCH 651/713 arch_numa: avoid false positive fortify warning in setup_node_to_cpumask_map()

In reply to Greg Kroah-Hartman

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

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

From: Nathan Chancellor <[email protected]>

commit f2b1cb39d5ccab090d8353788f186f7e7a1fffd4 upstream.

When building ARCH=riscv using clang with CONFIG_FORTIFY_SOURCE and CONFIG_UBSAN_BOUNDS enabled, CONFIG_NR_CPUS > 64, and the default value of 2 for CONFIG_NODES_SHIFT, there is a compiletime warning from the fortify routines.

In file included from mm/arch_numa.c:11:
In file included from include/linux/acpi.h:14:
In file included from include/linux/resource_ext.h:11:
In file included from include/linux/slab.h:17:
In file included from include/linux/gfp.h:7:
In file included from include/linux/mmzone.h:8:
In file included from include/linux/spinlock.h:60:
In file included from include/linux/interrupt_rc.h:17:
In file included from include/linux/smp.h:13:
In file included from include/linux/cpumask.h:11:
In file included from include/linux/bitmap.h:13:
In file included from include/linux/string.h:383:
include/linux/fortify-string.h:430:4: warning: call to '__write_overflow_field' declared with 'warning' attribute: detected write beyond size of field (1st parameter); maybe use struct_group()? [-Wattribue-warning]

    430 |                         __write_overflow_field(p_size_field, size);
        |                         ^
  include/linux/fortify-string.h:430:4: note: called by function 'fortify_memset_chk(unsigned long, unsigned long, unsigned long)'
  include/linux/bitmap.h:248:3: note: inlined by function 'setup_node_to_cpumask_map'
    248 |                 memset(dst, 0, len);
        |                 ^
  include/linux/fortify-string.h:462:25: note: expanded from macro 'memset'
    462 | #define memset(p, c, s) __fortify_memset_chk(p, c, s,                   \
        |                         ^
  include/linux/fortify-string.h:453:2: note: expanded from macro '__fortify_memset_chk'
    453 |         fortify_memset_chk(__fortify_size, p_size, p_size_field),       \
        |         ^
  include/linux/fortify-string.h:430:4: note: use '-gline-directives-only' (implied by '-g1') or higher for more accurate inlining chain locations
    430 |                         __write_overflow_field(p_size_field, size);
        |                         ^

1 warning generated.

In this configuration, MAX_NUMNODES is 4. clang unrolls the for loop in setup_node_to_cpumask_map() past this, which triggers the fortify check when accessing node_to_cpumask_map on the theoretical fifth loop iteration because it would be an out of bounds write.

Make it clear to clang that nr_node_ids is bounded by MAX_NUMNODES due to the logic in setup_nr_node_ids() by early returning in setup_node_to_cpumask_map() should that condition be violated.

---
 drivers/base/arch_numa.c |   12 ++++++++++++
 1 file changed, 12 insertions(+)
drivers/base/arch_numa.c +12 -0
@@ -105,6 +105,18 @@ static void __init setup_node_to_cpumask
 	if (nr_node_ids == MAX_NUMNODES)
 		setup_nr_node_ids();
 
+	/*
+	 * This check should never be true but it makes it clear to compilers
+	 * that node_to_cpumask_map is bound by nr_node_ids, avoiding false
+	 * positive fortify warnings when accessing node_to_cpumask_map in the
+	 * for loop below.
+	 */
+	if (unlikely(nr_node_ids > MAX_NUMNODES)) {
+		pr_err("nr_node_ids (%u) is larger than MAX_NUMNODES (%u)\n",
+		       nr_node_ids, MAX_NUMNODES);
+		return;
+	}
+
 	/* allocate and clear the mapping */
 	for (node = 0; node < nr_node_ids; node++) {
 		alloc_bootmem_cpumask_var(&node_to_cpumask_map[node]);
author maintainer
#658 lore raw

PATCH 652/713 dm-stats: fix a crash if allocation of per-cpu data fails

In reply to Greg Kroah-Hartman

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

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

From: Mikulas Patocka <[email protected]>

commit cc87e26d9cce22061dc21e51e11afef29dbbc36a upstream.

If "dm_kvzalloc(percpu_alloc_size, cpu_to_node(cpu))" fails, the code jumps to the "out" label and calls dm_stat_free. dm_stat_free does "for_each_possible_cpu(cpu) { dm_kvfree(s->stat_percpu[cpu][0].histogram, s->histogram_alloc_size);", which crashes with NULL pointer dereference if s->stat_percpu[cpu] is NULL.

This commit fixes the bug by testing s->stat_percpu[cpu] for NULL before using it.

Fixes fd2ed4d25270 ("dm: add statistics support")
---
 drivers/md/dm-stats.c |    6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)
drivers/md/dm-stats.c +4 -2
@@ -178,8 +178,10 @@ static void dm_stat_free(struct rcu_head
 	kfree(s->program_id);
 	kfree(s->aux_data);
 	for_each_possible_cpu(cpu) {
-		dm_kvfree(s->stat_percpu[cpu][0].histogram, s->histogram_alloc_size);
-		dm_kvfree(s->stat_percpu[cpu], s->percpu_alloc_size);
+		if (s->stat_percpu[cpu]) {
+			dm_kvfree(s->stat_percpu[cpu][0].histogram, s->histogram_alloc_size);
+			dm_kvfree(s->stat_percpu[cpu], s->percpu_alloc_size);
+		}
 	}
 	dm_kvfree(s->stat_shared[0].tmp.histogram, s->histogram_alloc_size);
 	dm_kvfree(s, s->shared_alloc_size);
author maintainer
#659 lore raw

PATCH 653/713 dm-switch: use WRITE_ONCE() in switch_region_table_write()

In reply to Greg Kroah-Hartman

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

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

From: Haotian Zhang <[email protected]>

commit c7391ebe33162c7962b313caea4d8e6b0bc2a671 upstream.

switch_region_table_read() accesses the region table with READ_ONCE() and is called from the lockless switch_map() IO path. However, switch_region_table_write() stores to the same array with a plain assignment. This results in an inconsistent access pattern for a lockless shared variable and may trigger data race reports.

Use WRITE_ONCE() to pair with the existing READ_ONCE() in switch_region_table_read().

Fixes 99eb1908e643 ("dm switch: factor out switch_region_table_read")
---
 drivers/md/dm-switch.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/md/dm-switch.c +1 -1
@@ -184,7 +184,7 @@ static void switch_region_table_write(st
 	pte = sctx->region_table[region_index];
 	pte &= ~((((region_table_slot_t)1 << sctx->region_table_entry_bits) - 1) << bit);
 	pte |= (region_table_slot_t)value << bit;
-	sctx->region_table[region_index] = pte;
+	WRITE_ONCE(sctx->region_table[region_index], pte);
 }
 
 /*
author maintainer
#660 lore raw

PATCH 654/713 dm-pcache: validate geometry fields from on-disk cache_info

In reply to Greg Kroah-Hartman

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

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

From: Bryam Vargas <[email protected]>

commit 32d1809da31094ef76fd98dc1f1a8b55ca1295dd upstream.

cache_segs_init() iterates cache_info->n_segs times indexing cache->segments[], which is sized to the cache device geometry, and get_seg_id() takes each segment id from the on-media cache_info and the per-segment next_seg link. Both come from cache device metadata that is only CRC-protected with a fixed public seed, so whoever supplies the cache device on a table load (CAP_SYS_ADMIN) controls them: an oversized n_segs or an out-of-range id drives an out-of-bounds access of cache->segments[] and a wild CACHE_DEV_SEGMENT() pointer into the device mapping -- an out-of-bounds read and write from on-disk data.

Reject an n_segs that exceeds the device segment count and a segment id that is out of range before either is used. Valid metadata is unaffected.

Fixes 1d57628ff95b ("dm-pcache: add persistent cache target in device-mapper")
---
 drivers/md/dm-pcache/cache.c |   14 ++++++++++++++
 1 file changed, 14 insertions(+)
drivers/md/dm-pcache/cache.c +14 -0
@@ -247,6 +247,13 @@ static int get_seg_id(struct pcache_cach
 		} else {
 			*seg_id = cache->cache_info.seg_id;
 		}
+
+		if (*seg_id >= cache_dev->seg_num) {
+			pcache_dev_err(pcache, "invalid segment id %u from cache device (seg_num %u)\n",
+				       *seg_id, cache_dev->seg_num);
+			ret = -EIO;
+			goto err;
+		}
 	}
 	return 0;
 err:
@@ -262,6 +269,13 @@ static int cache_segs_init(struct pcache
 	int ret;
 	u32 i;
 
+	if (cache_info->n_segs > cache->cache_dev->seg_num) {
+		pcache_dev_err(CACHE_TO_PCACHE(cache),
+			       "cache_info n_segs %u exceeds cache device segments %u\n",
+			       cache_info->n_segs, cache->cache_dev->seg_num);
+		return -EIO;
+	}
+
 	for (i = 0; i < cache_info->n_segs; i++) {
 		ret = get_seg_id(cache, prev_cache_seg, new_cache, &seg_id);
 		if (ret)
author maintainer
#661 lore raw

PATCH 655/713 dm-pcache: validate kset key_num and intra-segment bounds

In reply to Greg Kroah-Hartman

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

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

From: Bryam Vargas <[email protected]>

commit f11deb032fd84081e7831cffcba895d893054a22 upstream.

Two more fields decoded from the cache device go unbounded. The kset key_num drives cache_kset_crc() and the replay loop in cache_replay(), the writeback worker and the GC worker, but only the magic and a fixed-seed CRC are checked first, so a non-last kset whose key_num exceeds the PCACHE_KSET_KEYS_MAX buffer reads past its end before the CRC compare. A key's intra-segment offset and length in cache_key_decode() are taken verbatim, so a key running past its segment is replayed into the cache tree and the data CRC check and every later read hit then copy adjacent persistent memory into the caller's bio -- an out-of-bounds read that leaks to user space. Both fields are controlled by whoever supplies the cache device (CAP_SYS_ADMIN); the CRC seed is public.

Add kset_onmedia_valid() to bound key_num before any kset read, and reject a key whose offset plus length, computed in 64 bits, exceeds the segment data_size. Valid metadata is unaffected.

Fixes 1d57628ff95b ("dm-pcache: add persistent cache target in device-mapper")
---
 drivers/md/dm-pcache/cache.h           |   21 +++++++++++++++++++++
 drivers/md/dm-pcache/cache_gc.c        |    8 ++++----
 drivers/md/dm-pcache/cache_key.c       |   10 +++++++++-
 drivers/md/dm-pcache/cache_writeback.c |    8 ++++----
 4 files changed, 38 insertions(+), 9 deletions(-)
drivers/md/dm-pcache/cache.h +21 -0
@@ -491,6 +491,27 @@ static inline u32 cache_key_data_crc(str
 	return crc32c(PCACHE_CRC_SEED, data, key->len);
 }
 
+/**
+ * kset_onmedia_valid - Validate a kset header read from the cache device.
+ * @kset_onmedia: Pointer to the kset copied from on-media metadata.
+ *
+ * The magic and CRC are attacker-computable (fixed public seed). A non-last
+ * kset stores key_num keys inline, and cache_kset_crc() and the replay loop
+ * read struct_size(.., data, key_num) bytes from a buffer sized for
+ * PCACHE_KSET_KEYS_MAX keys, so key_num must be bounded before any such use.
+ */
+static inline bool kset_onmedia_valid(struct pcache_cache_kset_onmedia *kset_onmedia)
+{
+	if (kset_onmedia->magic != PCACHE_KSET_MAGIC)
+		return false;
+
+	if (!(kset_onmedia->flags & PCACHE_KSET_FLAGS_LAST) &&
+	    kset_onmedia->key_num > PCACHE_KSET_KEYS_MAX)
+		return false;
+
+	return true;
+}
+
 static inline u32 cache_kset_crc(struct pcache_cache_kset_onmedia *kset_onmedia)
 {
 	u32 crc_size;
drivers/md/dm-pcache/cache_gc.c +4 -4
@@ -44,11 +44,11 @@ static bool need_gc(struct pcache_cache
 		return false;
 	}
 
-	/* Check if kset_onmedia is corrupted */
-	if (kset_onmedia->magic != PCACHE_KSET_MAGIC) {
-		pcache_dev_debug(pcache, "gc error: magic is not as expected. key_tail: %u:%u magic: %llx, expected: %llx\n",
+	/* Reject a corrupted or out-of-bounds kset before reading its keys */
+	if (!kset_onmedia_valid(kset_onmedia)) {
+		pcache_dev_debug(pcache, "gc error: invalid kset. key_tail: %u:%u magic: %llx, key_num: %u\n",
 					key_tail->cache_seg->cache_seg_id, key_tail->seg_off,
-					kset_onmedia->magic, PCACHE_KSET_MAGIC);
+					kset_onmedia->magic, kset_onmedia->key_num);
 		return false;
 	}
 
drivers/md/dm-pcache/cache_key.c +9 -1
@@ -97,6 +97,14 @@ int cache_key_decode(struct pcache_cache
 	key->cache_pos.cache_seg = &cache->segments[key_onmedia->cache_seg_id];
 	key->cache_pos.seg_off = key_onmedia->cache_seg_off;
 
+	if ((u64)key->cache_pos.seg_off + key->len >
+			key->cache_pos.cache_seg->segment.data_size) {
+		pcache_dev_err(pcache, "key seg_off %u + len %u exceeds segment data size %u\n",
+				key->cache_pos.seg_off, key->len,
+				key->cache_pos.cache_seg->segment.data_size);
+		return -EIO;
+	}
+
 	key->seg_gen = key_onmedia->seg_gen;
 	key->flags = key_onmedia->flags;
 
@@ -778,7 +786,7 @@ int cache_replay(struct pcache_cache *ca
 			goto out;
 		}
 
-		if (kset_onmedia->magic != PCACHE_KSET_MAGIC ||
+		if (!kset_onmedia_valid(kset_onmedia) ||
 				kset_onmedia->crc != cache_kset_crc(kset_onmedia)) {
 			break;
 		}
drivers/md/dm-pcache/cache_writeback.c +4 -4
@@ -55,11 +55,11 @@ static inline bool is_cache_clean(struct
 		return true;
 	}
 
-	/* Check if the magic number matches the expected value */
-	if (kset_onmedia->magic != PCACHE_KSET_MAGIC) {
-		pcache_dev_debug(pcache, "dirty_tail: %u:%u magic: %llx, not expected: %llx\n",
+	/* Reject a corrupted or out-of-bounds kset before reading its keys */
+	if (!kset_onmedia_valid(kset_onmedia)) {
+		pcache_dev_debug(pcache, "dirty_tail: %u:%u invalid kset magic: %llx, key_num: %u\n",
 				dirty_tail->cache_seg->cache_seg_id, dirty_tail->seg_off,
-				kset_onmedia->magic, PCACHE_KSET_MAGIC);
+				kset_onmedia->magic, kset_onmedia->key_num);
 		return true;
 	}
 
author maintainer
#662 lore raw

PATCH 656/713 dm-pcache: validate on-media seg_num against the cache device size

In reply to Greg Kroah-Hartman

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

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

From: Bryam Vargas <[email protected]>

commit 62d92e45abe9e087370f9fc5d876b95673aced34 upstream.

seg_num is read from the crc32c-only superblock, so whoever supplies the cache device on a table load (CAP_SYS_ADMIN) controls it. It sizes cache->segments[] and is the value every later on-media segment id is bounded against, yet it is never checked against the device. Because cache_dev->mapping is the direct map of the pmem, CACHE_DEV_SEGMENT() for a segment id past the device resolves to ordinary kernel memory beyond the mapping; a new-cache init reaching such an id has cache_seg_init() -> cache_dev_zero_range() memset() 12 KiB over that memory -- an out-of-bounds write into the kernel heap at table load. A zero seg_num makes the segment allocations ZERO_SIZE_PTR.

Reject a seg_num that is zero, larger than the device can hold, or larger than PCACHE_CACHE_SEGS_MAX before it is used.

Fixes 1d57628ff95b ("dm-pcache: add persistent cache target in device-mapper")
---
 drivers/md/dm-pcache/cache_dev.c |   22 +++++++++++++++++++++-
 1 file changed, 21 insertions(+), 1 deletion(-)
drivers/md/dm-pcache/cache_dev.c +21 -1
@@ -242,6 +242,8 @@ int cache_dev_start(struct dm_pcache *pc
 	struct pcache_cache_dev *cache_dev = &pcache->cache_dev;
 	struct pcache_sb sb;
 	bool format = false;
+	u32 seg_num;
+	u64 max_segs;
 	int ret;
 
 	mutex_init(&cache_dev->seg_lock);
@@ -269,7 +271,25 @@ int cache_dev_start(struct dm_pcache *pc
 		goto dax_release;
 
 	cache_dev->sb_flags = le32_to_cpu(sb.flags);
-	ret = cache_dev_init(cache_dev, le32_to_cpu(sb.seg_num));
+
+	/*
+	 * seg_num is read from the crc32c-only superblock, so whoever supplies
+	 * the cache device controls it. It is the ceiling every later on-media
+	 * segment id is validated against, so bound it against what the device
+	 * physically holds before it is trusted, or a forged seg_num lets a
+	 * segment id address past the DAX mapping.
+	 */
+	seg_num = le32_to_cpu(sb.seg_num);
+	max_segs = (bdev_nr_bytes(cache_dev->dm_dev->bdev) - PCACHE_SEGMENTS_OFF) /
+		   PCACHE_SEG_SIZE;
+	if (seg_num == 0 || seg_num > max_segs || seg_num > PCACHE_CACHE_SEGS_MAX) {
+		pcache_dev_err(pcache, "invalid seg_num %u from cache device (device holds %llu, max %u)\n",
+			       seg_num, max_segs, (u32)PCACHE_CACHE_SEGS_MAX);
+		ret = -EIO;
+		goto dax_release;
+	}
+
+	ret = cache_dev_init(cache_dev, seg_num);
 	if (ret)
 		goto dax_release;
 
author maintainer
#663 lore raw

PATCH 657/713 dm-pcache: bound the persisted tail-position offset

In reply to Greg Kroah-Hartman

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

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

From: Bryam Vargas <[email protected]>

commit d1898576090a10d2ac2715218a652e78fb65a6b0 upstream.

cache_pos_decode() takes the persisted key_tail and dirty_tail seg_off from the cache device and addresses within the segment with it. A seg_off at or past the segment data_size, controllable by whoever supplies the device (CAP_SYS_ADMIN), reads past the segment data.

Reject a decoded seg_off that is not below the segment data_size.

Fixes 1d57628ff95b ("dm-pcache: add persistent cache target in device-mapper")
---
 drivers/md/dm-pcache/cache.c |    4 ++++
 1 file changed, 4 insertions(+)
drivers/md/dm-pcache/cache.c +4 -0
@@ -119,6 +119,10 @@ int cache_pos_decode(struct pcache_cache
 		return -EIO;
 
 	pos->cache_seg = &cache->segments[latest.cache_seg_id];
+
+	if (latest.seg_off >= pos->cache_seg->segment.data_size)
+		return -EIO;
+
 	pos->seg_off = latest.seg_off;
 	*seq = latest.header.seq;
 	*index = (latest_addr - pos_onmedia);
author maintainer
#664 lore raw

PATCH 658/713 dm-pcache: clamp the tail kset read to the segment data region

In reply to Greg Kroah-Hartman

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

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

From: Bryam Vargas <[email protected]>

commit becf07e2b0053027495ecd671b1f82fb2e615f68 upstream.

The tail-kset read in cache_replay(), the writeback worker and the GC worker bounds its length by PCACHE_SEG_SIZE - seg_off, the raw segment size rather than the data region. A tail near the segment end reads past the segment data into the following control area.

Clamp the read to cache_seg_remain(), the data region.

Fixes 1d57628ff95b ("dm-pcache: add persistent cache target in device-mapper")
---
 drivers/md/dm-pcache/cache_gc.c        |    2 +-
 drivers/md/dm-pcache/cache_key.c       |    2 +-
 drivers/md/dm-pcache/cache_writeback.c |    2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)
drivers/md/dm-pcache/cache_gc.c +1 -1
@@ -37,7 +37,7 @@ static bool need_gc(struct pcache_cache
 
 	kset_onmedia = (struct pcache_cache_kset_onmedia *)cache->gc_kset_onmedia_buf;
 
-	to_copy = min(PCACHE_KSET_ONMEDIA_SIZE_MAX, PCACHE_SEG_SIZE - key_tail->seg_off);
+	to_copy = min(PCACHE_KSET_ONMEDIA_SIZE_MAX, cache_seg_remain(key_tail));
 	ret = copy_mc_to_kernel(kset_onmedia, key_addr, to_copy);
 	if (ret) {
 		pcache_dev_err(pcache, "error to read kset: %d", ret);
drivers/md/dm-pcache/cache_key.c +1 -1
@@ -779,7 +779,7 @@ int cache_replay(struct pcache_cache *ca
 	__set_bit(pos->cache_seg->cache_seg_id, cache->seg_map);
 
 	while (true) {
-		to_copy = min(PCACHE_KSET_ONMEDIA_SIZE_MAX, PCACHE_SEG_SIZE - pos->seg_off);
+		to_copy = min(PCACHE_KSET_ONMEDIA_SIZE_MAX, cache_seg_remain(pos));
 		ret = copy_mc_to_kernel(kset_onmedia, cache_pos_addr(pos), to_copy);
 		if (ret) {
 			ret = -EIO;
drivers/md/dm-pcache/cache_writeback.c +1 -1
@@ -48,7 +48,7 @@ static inline bool is_cache_clean(struct
 	addr = cache_pos_addr(dirty_tail);
 	kset_onmedia = (struct pcache_cache_kset_onmedia *)cache->wb_kset_onmedia_buf;
 
-	to_copy = min(PCACHE_KSET_ONMEDIA_SIZE_MAX, PCACHE_SEG_SIZE - dirty_tail->seg_off);
+	to_copy = min(PCACHE_KSET_ONMEDIA_SIZE_MAX, cache_seg_remain(dirty_tail));
 	ret = copy_mc_to_kernel(kset_onmedia, addr, to_copy);
 	if (ret) {
 		pcache_dev_err(pcache, "error to read kset: %d", ret);
author maintainer
#665 lore raw

PATCH 659/713 dm-pcache: detect a cycle in the last-kset chain during replay

In reply to Greg Kroah-Hartman

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

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

From: Bryam Vargas <[email protected]>

commit 16c3b3a326e70f246a605b3dc27b7f83ba4743e3 upstream.

cache_replay() follows the on-media last-kset chain by next_cache_seg_id with no cond_resched(). A forged chain that points back into a segment it has already visited makes the replay loop follow it forever.

Cap the last-kset hops at cache->n_segs; a valid chain visits each segment at most once.

Fixes 1d57628ff95b ("dm-pcache: add persistent cache target in device-mapper")
---
 drivers/md/dm-pcache/cache_key.c |    7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)
drivers/md/dm-pcache/cache_key.c +6 -1
@@ -762,7 +762,7 @@ int cache_replay(struct pcache_cache *ca
 	struct pcache_cache_pos pos_tail;
 	struct pcache_cache_pos *pos;
 	struct pcache_cache_kset_onmedia *kset_onmedia;
-	u32 to_copy, count = 0;
+	u32 to_copy, count = 0, last_hops = 0;
 	int ret = 0;
 
 	kset_onmedia = kzalloc(PCACHE_KSET_ONMEDIA_SIZE_MAX, GFP_KERNEL);
@@ -797,6 +797,11 @@ int cache_replay(struct pcache_cache *ca
 
 			pcache_dev_debug(pcache, "last kset replay, next: %u\n", kset_onmedia->next_cache_seg_id);
 
+			if (++last_hops > cache->n_segs) {
+				ret = -EIO;
+				goto out;
+			}
+
 			next_seg = &cache->segments[kset_onmedia->next_cache_seg_id];
 
 			pos->cache_seg = next_seg;
author maintainer
#666 lore raw

PATCH 660/713 dm-pcache: only hand out initialized cache segments

In reply to Greg Kroah-Hartman

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

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

From: Bryam Vargas <[email protected]>

commit 2df0fc042e299bae3c0f60ea5cd2af9285658e9f upstream.

get_cache_segment() scans the segment map up to cache->n_segs, the physical device segment count, but cache_segs_init() only initializes the first cache_info->n_segs segments. A crafted image with cache_info->n_segs smaller than the device count leaves the remaining pcache_cache_segment structs zeroed (segment.data == NULL), and the allocator can hand one to cache_kset_close(), which writes through the returned segment's data pointer with no NULL check.

Bound the allocator's search to cache_info->n_segs so only initialized segments are ever returned. A conforming cache sets n_segs equal to the device segment count, so this rejects nothing legitimate.

Fixes 1d57628ff95b ("dm-pcache: add persistent cache target in device-mapper")
---
 drivers/md/dm-pcache/cache_segment.c |   12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)
drivers/md/dm-pcache/cache_segment.c +10 -2
@@ -243,8 +243,16 @@ struct pcache_cache_segment *get_cache_s
 
 	spin_lock(&cache->seg_map_lock);
 again:
-	seg_id = find_next_zero_bit(cache->seg_map, cache->n_segs, cache->last_cache_seg);
-	if (seg_id == cache->n_segs) {
+	/*
+	 * Only allocate initialized segments. cache_segs_init() initializes
+	 * cache_info.n_segs of the cache->n_segs device segments; a forged
+	 * smaller cache_info.n_segs leaves the rest as zeroed structs whose data
+	 * pointer is NULL. Bounding the search to cache_info.n_segs keeps such a
+	 * segment from reaching cache_kset_close(), which writes through it.
+	 */
+	seg_id = find_next_zero_bit(cache->seg_map, cache->cache_info.n_segs,
+				    cache->last_cache_seg);
+	if (seg_id == cache->cache_info.n_segs) {
 		/* reset the hint of ->last_cache_seg and retry */
 		if (cache->last_cache_seg) {
 			cache->last_cache_seg = 0;
author maintainer
#667 lore raw

PATCH 661/713 dm-pcache: fix implicit u8 truncation of gc_percent in message handler

In reply to Greg Kroah-Hartman

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

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

From: Jianyun Gao <[email protected]>

commit fb9e17287a4ea1cbbcedc77e6866978ecc2a7b55 upstream.

When setting gc_percent via message, kstrtoul parses the input into an unsigned long, which is then implicitly truncated to u8 when passed to pcache_cache_set_gc_percent(). For example, value 266 (0x10A) silently truncates to 10 (0x0A), successfully bypassing the > 90 upper bound check in pcache_cache_set_gc_percent(), and setting a different value than the user intended.

Use kstrtou8 directly instead of kstrtoul, so that overflow values are properly rejected.

Fixes 1d57628ff95b ("dm-pcache: add persistent cache target in device-mapper")
---
 drivers/md/dm-pcache/dm_pcache.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/md/dm-pcache/dm_pcache.c +2 -2
@@ -439,13 +439,13 @@ static int dm_pcache_message(struct dm_t
 			     char **argv, char *result, unsigned int maxlen)
 {
 	struct dm_pcache *pcache = ti->private;
-	unsigned long val;
+	u8 val;
 
 	if (argc != 2)
 		goto err;
 
 	if (!strcasecmp(argv[0], "gc_percent")) {
-		if (kstrtoul(argv[1], 10, &val))
+		if (kstrtou8(argv[1], 10, &val))
 			goto err;
 
 		return pcache_cache_set_gc_percent(&pcache->cache, val);
author maintainer
#668 lore raw

PATCH 662/713 dm-pcache: fix use-after-free and invalid seg operations in kset_replay()

In reply to Greg Kroah-Hartman

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

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

From: Jianyun Gao <[email protected]>

commit c2e894eac398b258f12fdec73ed6ba081047f7b3 upstream.

In kset_replay, when key->seg_gen is stale (key->seg_gen < key->cache_pos.cache_seg->gen), cache_key_put(key) is called but then key->cache_pos.cache_seg is accessed as the argument to cache_seg_get(). This is a use-after-free on the freed key memory. Although mempool recycled memory is not immediately reclaimed or overwritten in practice, this is still a potential UAF bug.

Additionally, for expired invalid keys, setting the cache->seg_map bit and calling cache_seg_get() is unreasonable since the corresponding segment data is no longer valid.

Fix both issues by moving cache_seg_get() and __set_bit() after the gen check, so they only execute for valid keys, and using continue to skip invalid keys.

Fixes 1d57628ff95b ("dm-pcache: add persistent cache target in device-mapper")
---
 drivers/md/dm-pcache/cache_key.c |   13 ++++++-------
 1 file changed, 6 insertions(+), 7 deletions(-)
drivers/md/dm-pcache/cache_key.c +6 -7
@@ -736,18 +736,17 @@ static int kset_replay(struct pcache_cac
 			goto err;
 		}
 
-		__set_bit(key->cache_pos.cache_seg->cache_seg_id, cache->seg_map);
-
 		/* Check if the segment generation is valid for insertion. */
 		if (key->seg_gen < key->cache_pos.cache_seg->gen) {
 			cache_key_put(key);
-		} else {
-			cache_subtree = get_subtree(&cache->req_key_tree, key->off);
-			spin_lock(&cache_subtree->tree_lock);
-			cache_key_insert(&cache->req_key_tree, key, true);
-			spin_unlock(&cache_subtree->tree_lock);
+			continue;
 		}
 
+		__set_bit(key->cache_pos.cache_seg->cache_seg_id, cache->seg_map);
+		cache_subtree = get_subtree(&cache->req_key_tree, key->off);
+		spin_lock(&cache_subtree->tree_lock);
+		cache_key_insert(&cache->req_key_tree, key, true);
+		spin_unlock(&cache_subtree->tree_lock);
 		cache_seg_get(key->cache_pos.cache_seg);
 	}
 
author maintainer
#669 lore raw

PATCH 663/713 i3c: Fix unlocked dereference of dev->desc in i3c_device_get_supported_xfer_mode()

In reply to Greg Kroah-Hartman

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

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

From: Adrian Hunter <[email protected]>

commit 8bed7f4fa710914b7f05fd59998316bfb4d43385 upstream.

i3c_device_get_supported_xfer_mode() uses dev->desc to obtain the master controller. However, dev->desc must not be dereferenced unless bus->lock is held, and this function does not take that lock.

The function only needs access to the master controller associated with the device's bus. Use dev->bus instead, which is always valid for the lifetime of the device and does not require dereferencing dev->desc.

---
 drivers/i3c/device.c    |    2 +-
 drivers/i3c/internals.h |    5 +++++
 drivers/i3c/master.c    |    6 ------
 3 files changed, 6 insertions(+), 7 deletions(-)
drivers/i3c/device.c +1 -1
@@ -309,7 +309,7 @@ EXPORT_SYMBOL_GPL(i3c_device_match_id);
  */
 u32 i3c_device_get_supported_xfer_mode(struct i3c_device *dev)
 {
-	return i3c_dev_get_master(dev->desc)->this->info.hdr_cap | BIT(I3C_SDR);
+	return i3c_bus_to_i3c_master(dev->bus)->this->info.hdr_cap | BIT(I3C_SDR);
 }
 EXPORT_SYMBOL_GPL(i3c_device_get_supported_xfer_mode);
 
drivers/i3c/internals.h +5 -0
@@ -72,4 +72,9 @@ static inline void i3c_readl_fifo(const
 	}
 }
 
+static inline struct i3c_master_controller *i3c_bus_to_i3c_master(struct i3c_bus *i3cbus)
+{
+	return container_of(i3cbus, struct i3c_master_controller, bus);
+}
+
 #endif /* I3C_INTERNAL_H */
drivers/i3c/master.c +0 -6
@@ -97,12 +97,6 @@ void i3c_bus_normaluse_unlock(struct i3c
 	up_read(&bus->lock);
 }
 
-static struct i3c_master_controller *
-i3c_bus_to_i3c_master(struct i3c_bus *i3cbus)
-{
-	return container_of(i3cbus, struct i3c_master_controller, bus);
-}
-
 static struct i3c_master_controller *dev_to_i3cmaster(struct device *dev)
 {
 	return container_of(dev, struct i3c_master_controller, dev);
author maintainer
#670 lore raw

PATCH 664/713 i3c: master: adi: initialize the lock before enabling interrupts

In reply to Greg Kroah-Hartman

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

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

From: Runyu Xiao <[email protected]>

commit 8a53f9102a0d3eeb8784999f925028acf339c276 upstream.

adi_i3c_master_probe() requests the IRQ and unmasks REG_IRQ_PENDING_CMDR before the controller's IBI state, transfer queue list and transfer queue lock are initialized. A pending CMDR interrupt can therefore run adi_i3c_master_irq() and take master->xferqueue.lock before the dynamic lock has been initialized.

This issue was found by our static analysis tool and then manually reviewed against the current tree.

The grounded PoC kept the probe ordering and the IRQ path adi_i3c_master_probe() -> adi_i3c_master_irq() -> xferqueue.lock, with a pending CMDR interrupt arriving after REG_IRQ_PENDING_CMDR is unmasked. Lockdep reported:

INFO: trying to register non-static key.
you didn't initialize this object before use?

  lock_acquire+0xbb/0x290
  _raw_spin_lock_irqsave+0x36/0x60
  adi_i3c_master_irq+0x32/0x56 [vuln_msv]
  adi_i3c_master_probe+0x5a/0xf47 [vuln_msv]

Initialize the transfer queue and IBI state before requesting and unmasking the IRQ.

Fixes a79ac2cdc91d ("i3c: master: Add driver for Analog Devices I3C Controller IP")
---
 drivers/i3c/master/adi-i3c-master.c |   15 +++++++--------
 1 file changed, 7 insertions(+), 8 deletions(-)
drivers/i3c/master/adi-i3c-master.c +7 -8
@@ -964,17 +964,9 @@ static int adi_i3c_master_probe(struct p
 	writel(0x00, master->regs + REG_ENABLE);
 	writel(0x00, master->regs + REG_IRQ_MASK);
 
-	ret = devm_request_irq(&pdev->dev, irq, adi_i3c_master_irq, 0,
-			       dev_name(&pdev->dev), master);
-	if (ret)
-		return ret;
-
 	platform_set_drvdata(pdev, master);
 
 	master->free_rr_slots = GENMASK(ADI_MAX_DEVS, 1);
-
-	writel(REG_IRQ_PENDING_CMDR, master->regs + REG_IRQ_MASK);
-
 	spin_lock_init(&master->ibi.lock);
 	master->ibi.num_slots = 15;
 	master->ibi.slots = devm_kcalloc(&pdev->dev, master->ibi.num_slots,
@@ -986,6 +978,13 @@ static int adi_i3c_master_probe(struct p
 	spin_lock_init(&master->xferqueue.lock);
 	INIT_LIST_HEAD(&master->xferqueue.list);
 
+	ret = devm_request_irq(&pdev->dev, irq, adi_i3c_master_irq, 0,
+			       dev_name(&pdev->dev), master);
+	if (ret)
+		return ret;
+
+	writel(REG_IRQ_PENDING_CMDR, master->regs + REG_IRQ_MASK);
+
 	return i3c_master_register(&master->base, &pdev->dev,
 				   &adi_i3c_master_ops, false);
 }
author maintainer
#671 lore raw

PATCH 665/713 i3c: master: Fix info leak and UAF in device unregister path

In reply to Greg Kroah-Hartman

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

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

From: Adrian Hunter <[email protected]>

commit d2c743efd2d1ee64e94324664808f623dd865872 upstream.

i3c_master_unregister_i3c_devs() clears i3cdev->dev->desc before calling device_unregister(). During device_unregister(), device_del() emits a KOBJ_REMOVE uevent and unbinds the driver while the device descriptor is still expected to be valid. As a result, i3c_device_uevent() and a racing modalias_show() can observe a NULL desc and fall back to an uninitialized stack struct i3c_device_info, leaking kernel stack contents in the generated modalias. Driver .remove() callbacks may also encounter an unexpected NULL desc during unbind.

Keep desc valid until device_unregister() has completed. Since device_unregister() drops the device reference and may free the device, take an extra reference with get_device() before unregistering. Clear desc afterwards and release the extra reference with put_device(). This preserves the release-time invariant that desc must be NULL while avoiding both the information leak and a potential use-after-free from writing desc after the device has been released.

---
 drivers/i3c/master.c |    9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)
drivers/i3c/master.c +5 -4
@@ -2971,11 +2971,12 @@ static void i3c_master_unregister_i3c_de
 		if (!i3cdev->dev)
 			continue;
 
-		i3cdev->dev->desc = NULL;
-		if (device_is_registered(&i3cdev->dev->dev))
+		if (device_is_registered(&i3cdev->dev->dev)) {
+			get_device(&i3cdev->dev->dev);
 			device_unregister(&i3cdev->dev->dev);
-		else
-			put_device(&i3cdev->dev->dev);
+		}
+		i3cdev->dev->desc = NULL;
+		put_device(&i3cdev->dev->dev);
 		i3cdev->dev = NULL;
 	}
 }
author maintainer
#672 lore raw

PATCH 666/713 i3c: master: svc: bound IBI payload to the requested max_payload_len

In reply to Greg Kroah-Hartman

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

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

From: Maoyi Xie <[email protected]>

commit e2bda39d7f9f285ec803e200b5c1f17143d0b483 upstream.

svc_i3c_master_handle_ibi() reads the IBI payload from the RX FIFO into the IBI slot. The loop is bounded by the hardware FIFO size (SVC_I3C_FIFO_SIZE), not by the slot size.

slot->data points into the IBI pool, which i3c_generic_ibi_alloc_pool() sizes at max_payload_len per slot. svc_i3c_master_request_ibi() only rejects a max_payload_len larger than SVC_I3C_FIFO_SIZE, so a driver can request a smaller one. mctp-i3c requests 1. Each readsb() then copies the controller RXCOUNT bytes (up to 31) with no check against the slot size. A device that sends more bytes than the slot holds writes past slot->data, an out-of-bounds write into the IBI pool.

Bound the loop by dev->ibi->max_payload_len and clamp each read to the space left in the slot, the same way dw-i3c does. A device can still send more than the requested payload. Flush the leftover bytes from the RX FIFO so they do not leak into the next transfer.

---
 drivers/i3c/master/svc-i3c-master.c |   10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)
drivers/i3c/master/svc-i3c-master.c +9 -1
@@ -455,14 +455,22 @@ static int svc_i3c_master_handle_ibi(str
 	buf = slot->data;
 
 	while (SVC_I3C_MSTATUS_RXPEND(readl(master->regs + SVC_I3C_MSTATUS))  &&
-	       slot->len < SVC_I3C_FIFO_SIZE) {
+	       slot->len < dev->ibi->max_payload_len) {
 		mdatactrl = readl(master->regs + SVC_I3C_MDATACTRL);
 		count = SVC_I3C_MDATACTRL_RXCOUNT(mdatactrl);
+		count = min(count, dev->ibi->max_payload_len - slot->len);
 		readsb(master->regs + SVC_I3C_MRDATAB, buf, count);
 		slot->len += count;
 		buf += count;
 	}
 
+	/*
+	 * The device may have sent more than the requested payload. Drop the
+	 * extra bytes so they do not leak into the next transfer.
+	 */
+	if (SVC_I3C_MSTATUS_RXPEND(readl(master->regs + SVC_I3C_MSTATUS)))
+		writel(SVC_I3C_MDATACTRL_FLUSHRB, master->regs + SVC_I3C_MDATACTRL);
+
 	master->ibi.tbq_slot = slot;
 
 	return 0;
author maintainer
#673 lore raw

PATCH 667/713 i3c: master: Fix potential UAF in i3c_device_uevent()

In reply to Greg Kroah-Hartman

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

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

From: Adrian Hunter <[email protected]>

commit e5e8dd2e959f470524c16ca444d001c90d6bb3ad upstream.

i3c_device_uevent() dereferences i3cdev->desc without holding the bus normal-use lock. Since the descriptor pointer can be replaced concurrently, including when a uevent is generated from sysfs, this can result in dereferencing a stale descriptor and lead to a use-after-free.

Use i3c_device_get_info() instead, which protects access to the descriptor with the normal-use lock.

Commit 6cf7b65f7029 ("i3c: Use i3cdev->desc->info instead of calling i3c_device_get_info() to avoid deadlock") replaced the accessor with a direct descriptor dereference because i3c_device_get_info() would recursively acquire bus->lock during device registration.

This change depends on "i3c: master: Fix recursive locking during device registration", which moves device registration out from under bus->lock and removes the possibility of that deadlock. Without that change, restoring the i3c_device_get_info() call would reintroduce the deadlock.

Fixes 6cf7b65f7029 ("i3c: Use i3cdev->desc->info instead of calling i3c_device_get_info() to avoid deadlock")
Cc [email protected] # requires "i3c: master: Fix recursive locking during device registration"
---
 drivers/i3c/master.c |    3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)
drivers/i3c/master.c +1 -2
@@ -311,8 +311,7 @@ static int i3c_device_uevent(const struc
 	struct i3c_device_info devinfo;
 	u16 manuf, part, ext;
 
-	if (i3cdev->desc)
-		devinfo = i3cdev->desc->info;
+	i3c_device_get_info(i3cdev, &devinfo);
 	manuf = I3C_PID_MANUF_ID(devinfo.pid);
 	part = I3C_PID_PART_ID(devinfo.pid);
 	ext = I3C_PID_EXTRA_INFO(devinfo.pid);
author maintainer
#674 lore raw

PATCH 668/713 i3c: renesas: Check that the transfer is valid before accessing it

In reply to Greg Kroah-Hartman

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

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

From: Claudiu Beznea <[email protected]>

commit 5f1a76ecfe90544a28d657306c9b3caa66ba0e63 upstream.

The Renesas I3C driver uses an asynchronous model to transfer data. It prepares a struct renesas_i3c_xfer, enqueues it, and waits for completion. The interrupt handler dequeues the transfer, updates/uses it, and signals the waiting thread.

If the completion times out, the waiting thread dequeues the transfer and free it. If an interrupt fires after that, the handler may access freed memory, leading to crashes.

Check that the transfer is still valid before accessing it in the interrupt handler. With it clear any status flags and disable all the interrupts to avoid triggering the same interrupts again.

---
 drivers/i3c/master/renesas-i3c.c |   52 +++++++++++++++++++++++++++++++++------
 1 file changed, 45 insertions(+), 7 deletions(-)
drivers/i3c/master/renesas-i3c.c +45 -7
@@ -433,6 +433,21 @@ static void renesas_i3c_enqueue_xfer(str
 	}
 }
 
+static void renesas_i3c_irqs_mask_and_clear_locked(struct renesas_i3c *i3c)
+{
+	/* Disable all the interrupts. */
+	renesas_writel(i3c->regs, BIE, 0);
+	renesas_writel(i3c->regs, NTIE, 0);
+
+	/* Clear normal transfer status flags. */
+	renesas_writel(i3c->regs, NTST, 0);
+
+	/* Clear bus status flags. */
+	renesas_writel(i3c->regs, BST, 0);
+	/* Read back registers to confirm writes have fully propagated. */
+	renesas_readl(i3c->regs, BST);
+}
+
 static void renesas_i3c_wait_xfer(struct renesas_i3c *i3c, struct renesas_i3c_xfer *xfer)
 {
 	unsigned long time_left;
@@ -1018,6 +1033,11 @@ static irqreturn_t renesas_i3c_tx_isr(in
 
 	scoped_guard(spinlock, &i3c->xferqueue.lock) {
 		xfer = i3c->xferqueue.cur;
+		if (!xfer) {
+			renesas_i3c_irqs_mask_and_clear_locked(i3c);
+			return IRQ_HANDLED;
+		}
+
 		cmd = xfer->cmds;
 
 		if (xfer->is_i2c_xfer) {
@@ -1058,6 +1078,11 @@ static irqreturn_t renesas_i3c_resp_isr(
 
 	scoped_guard(spinlock, &i3c->xferqueue.lock) {
 		xfer = i3c->xferqueue.cur;
+		if (!xfer) {
+			renesas_i3c_irqs_mask_and_clear_locked(i3c);
+			return IRQ_HANDLED;
+		}
+
 		cmd = xfer->cmds;
 
 		/* Clear the Respone Queue Full status flag*/
@@ -1142,6 +1167,11 @@ static irqreturn_t renesas_i3c_tend_isr(
 
 	scoped_guard(spinlock, &i3c->xferqueue.lock) {
 		xfer = i3c->xferqueue.cur;
+		if (!xfer) {
+			renesas_i3c_irqs_mask_and_clear_locked(i3c);
+			return IRQ_HANDLED;
+		}
+
 		cmd = xfer->cmds;
 
 		if (xfer->is_i2c_xfer) {
@@ -1188,6 +1218,11 @@ static irqreturn_t renesas_i3c_rx_isr(in
 
 	scoped_guard(spinlock, &i3c->xferqueue.lock) {
 		xfer = i3c->xferqueue.cur;
+		if (!xfer) {
+			renesas_i3c_irqs_mask_and_clear_locked(i3c);
+			return IRQ_HANDLED;
+		}
+
 		cmd = xfer->cmds;
 
 		if (xfer->is_i2c_xfer) {
@@ -1238,15 +1273,13 @@ static irqreturn_t renesas_i3c_stop_isr(
 	struct renesas_i3c_xfer *xfer;
 
 	scoped_guard(spinlock, &i3c->xferqueue.lock) {
-		xfer = i3c->xferqueue.cur;
-
-		/* read back registers to confirm writes have fully propagated */
-		renesas_writel(i3c->regs, BST, 0);
-		renesas_readl(i3c->regs, BST);
-		renesas_writel(i3c->regs, BIE, 0);
-		renesas_clear_bit(i3c->regs, NTST, NTST_TDBEF0 | NTST_RDBFF0);
+		renesas_i3c_irqs_mask_and_clear_locked(i3c);
 		renesas_clear_bit(i3c->regs, SCSTRCTL, SCSTRCTL_RWE);
 
+		xfer = i3c->xferqueue.cur;
+		if (!xfer)
+			return IRQ_HANDLED;
+
 		xfer->ret = 0;
 		complete(&xfer->comp);
 	}
@@ -1263,6 +1296,11 @@ static irqreturn_t renesas_i3c_start_isr
 
 	scoped_guard(spinlock, &i3c->xferqueue.lock) {
 		xfer = i3c->xferqueue.cur;
+		if (!xfer) {
+			renesas_i3c_irqs_mask_and_clear_locked(i3c);
+			return IRQ_HANDLED;
+		}
+
 		cmd = xfer->cmds;
 
 		if (xfer->is_i2c_xfer) {
author maintainer
#675 lore raw

PATCH 669/713 i3c: renesas: Clean DATBAS register on detach

In reply to Greg Kroah-Hartman

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

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

From: Claudiu Beznea <[email protected]>

commit 797ed83c0cd495be4b345750c59e0363bf4d6207 upstream.

The controller uses DATBAS registers on TX/RX logic. Clean the DATBAS register for the detached I3C device to avoid issues.

---
 drivers/i3c/master/renesas-i3c.c |    2 ++
 1 file changed, 2 insertions(+)
drivers/i3c/master/renesas-i3c.c +2 -0
@@ -924,6 +924,8 @@ static void renesas_i3c_detach_i3c_dev(s
 	struct i3c_master_controller *m = i3c_dev_get_master(dev);
 	struct renesas_i3c *i3c = to_renesas_i3c(m);
 
+	renesas_writel(i3c->regs, DATBAS(data->index), 0);
+
 	i3c_dev_set_master_data(dev, NULL);
 	i3c->addrs[data->index] = 0;
 	i3c->free_pos |= BIT(data->index);
author maintainer
#676 lore raw

PATCH 670/713 i3c: renesas: Follow the reset deassert order used in probe

In reply to Greg Kroah-Hartman

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

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

From: Claudiu Beznea <[email protected]>

commit 7b15ca2615ec41fb428eb9af71bf3ebc95021201 upstream.

Use the same reset deassert order in the resume and probe paths to avoid potential failures due to ordering differences.

---
 drivers/i3c/master/renesas-i3c.c |   12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)
drivers/i3c/master/renesas-i3c.c +6 -6
@@ -1461,17 +1461,17 @@ static int renesas_i3c_resume_noirq(stru
 	struct renesas_i3c *i3c = dev_get_drvdata(dev);
 	int i, ret;
 
-	ret = reset_control_deassert(i3c->presetn);
+	ret = reset_control_deassert(i3c->tresetn);
 	if (ret)
 		return ret;
 
-	ret = reset_control_deassert(i3c->tresetn);
+	ret = reset_control_deassert(i3c->presetn);
 	if (ret)
-		goto err_presetn;
+		goto err_tresetn;
 
 	ret = clk_bulk_enable(i3c->num_clks, i3c->clks);
 	if (ret)
-		goto err_tresetn;
+		goto err_presetn;
 
 	/* Re-store I3C registers value. */
 	renesas_writel(i3c->regs, REFCKCTL,
@@ -1490,10 +1490,10 @@ static int renesas_i3c_resume_noirq(stru
 
 	return 0;
 
-err_tresetn:
-	reset_control_assert(i3c->tresetn);
 err_presetn:
 	reset_control_assert(i3c->presetn);
+err_tresetn:
+	reset_control_assert(i3c->tresetn);
 	return ret;
 }
 
author maintainer
#677 lore raw

PATCH 671/713 i3c: renesas: Reconfigure the DATBAS register on re-attach

In reply to Greg Kroah-Hartman

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

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

From: Claudiu Beznea <[email protected]>

commit 1364afd3e2e76e007a2c07ec95704d56980226f0 upstream.

During re-attach, the device may change its position in the i3c->addrs[] array. As a result, it may use a different Device Address Table Basic Register (DATBAS), which needs to be reconfigured.

Reconfigure the DATBAS register on re-attach. Along with it update software caches.

---
 drivers/i3c/master/renesas-i3c.c |   16 ++++++++++++++++
 1 file changed, 16 insertions(+)
drivers/i3c/master/renesas-i3c.c +16 -0
@@ -911,10 +911,26 @@ static int renesas_i3c_reattach_i3c_dev(
 	struct i3c_master_controller *m = i3c_dev_get_master(dev);
 	struct renesas_i3c *i3c = to_renesas_i3c(m);
 	struct renesas_i3c_i2c_dev_data *data = i3c_dev_get_master_data(dev);
+	int pos;
+
+	pos = renesas_i3c_get_free_pos(i3c);
+
+	if (data->index > pos && pos >= 0) {
+		renesas_writel(i3c->regs, DATBAS(data->index), 0);
+		i3c->addrs[data->index] = 0;
+		i3c->free_pos |= BIT(data->index);
+
+		data->index = pos;
+		i3c->free_pos &= ~BIT(data->index);
+	}
 
 	i3c->addrs[data->index] = dev->info.dyn_addr ? dev->info.dyn_addr :
 							dev->info.static_addr;
 
+	renesas_writel(i3c->regs, DATBAS(data->index),
+		       DATBAS_DVSTAD(dev->info.static_addr) |
+		       datbas_dvdyad_with_parity(i3c->addrs[data->index]));
+
 	return 0;
 }
 
author maintainer
#678 lore raw

PATCH 672/713 i3c: renesas: Reset the controller on resume

In reply to Greg Kroah-Hartman

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

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

From: Claudiu Beznea <[email protected]>

commit 27cf0ad162f1a4526a86fc2ec24d84a4a98ac1dd upstream.

Reset the controller on resume after enabling the clocks to follow the same sequence as in probe and avoid potential ordering related failures.

With it, renesas_i3c_reset() was updated to use read_poll_timeout_atomic(), as the driver's resume callback is executed during the noirq phase of resume, where interrupts are disabled.

---
 drivers/i3c/master/renesas-i3c.c |   10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)
drivers/i3c/master/renesas-i3c.c +8 -2
@@ -494,8 +494,8 @@ static int renesas_i3c_reset(struct rene
 	renesas_writel(i3c->regs, BCTL, 0);
 	renesas_set_bit(i3c->regs, RSTCTL, RSTCTL_RI3CRST);
 
-	return read_poll_timeout(renesas_readl, val, !(val & RSTCTL_RI3CRST),
-				 0, 1000, false, i3c->regs, RSTCTL);
+	return read_poll_timeout_atomic(renesas_readl, val, !(val & RSTCTL_RI3CRST),
+					0, 1000, false, i3c->regs, RSTCTL);
 }
 
 static void renesas_i3c_hw_init(struct renesas_i3c *i3c)
@@ -1489,6 +1489,10 @@ static int renesas_i3c_resume_noirq(stru
 	if (ret)
 		goto err_presetn;
 
+	ret = renesas_i3c_reset(i3c);
+	if (ret)
+		goto err_clks_disable;
+
 	/* Re-store I3C registers value. */
 	renesas_writel(i3c->regs, REFCKCTL,
 		       REFCKCTL_IREFCKS(i3c->refclk_div));
@@ -1506,6 +1510,8 @@ static int renesas_i3c_resume_noirq(stru
 
 	return 0;
 
+err_clks_disable:
+	clk_bulk_disable(i3c->num_clks, i3c->clks);
 err_presetn:
 	reset_control_assert(i3c->presetn);
 err_tresetn:
author maintainer
#679 lore raw

PATCH 673/713 i3c: renesas: Restore STDBR and EXTBR registers on resume

In reply to Greg Kroah-Hartman

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

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

From: Claudiu Beznea <[email protected]>

commit 21cded44e69c630799e65baadb72ad123d1347c8 upstream.

The Renesas RZ/G3S supports a power saving state where power to the most SoC componentes (including I3C) is lost.

The STDBR and EXTBR are configured in initialization phase though the struct i3c_master_controller_ops::bus_init. Set them on resume function as well to keep the same state of the controller after a suspend with power loss and a similar initialization sequence as in bus_init.

---
 drivers/i3c/master/renesas-i3c.c |   10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)
drivers/i3c/master/renesas-i3c.c +6 -4
@@ -260,6 +260,7 @@ struct renesas_i3c {
 	u32 dyn_addr;
 	u32 i2c_STDBR;
 	u32 i3c_STDBR;
+	u32 extbr;
 	unsigned long rate;
 	u8 addrs[RENESAS_I3C_MAX_DEVS];
 	struct renesas_i3c_xferqueue xferqueue;
@@ -622,10 +623,9 @@ static int renesas_i3c_bus_init(struct i
 	renesas_writel(i3c->regs, STDBR, i3c->i3c_STDBR);
 
 	/* Extended Bit Rate setting */
-	renesas_writel(i3c->regs, EXTBR, EXTBR_EBRLO(od_low_ticks) |
-					   EXTBR_EBRHO(od_high_ticks) |
-					   EXTBR_EBRLP(pp_low_ticks) |
-					   EXTBR_EBRHP(pp_high_ticks));
+	i3c->extbr = EXTBR_EBRLO(od_low_ticks) | EXTBR_EBRHO(od_high_ticks) |
+		     EXTBR_EBRLP(pp_low_ticks) | EXTBR_EBRHP(pp_high_ticks);
+	renesas_writel(i3c->regs, EXTBR, i3c->extbr);
 
 	renesas_writel(i3c->regs, REFCKCTL, REFCKCTL_IREFCKS(cks));
 	i3c->refclk_div = cks;
@@ -1494,6 +1494,8 @@ static int renesas_i3c_resume_noirq(stru
 		goto err_clks_disable;
 
 	/* Re-store I3C registers value. */
+	renesas_writel(i3c->regs, STDBR, i3c->i3c_STDBR);
+	renesas_writel(i3c->regs, EXTBR, i3c->extbr);
 	renesas_writel(i3c->regs, REFCKCTL,
 		       REFCKCTL_IREFCKS(i3c->refclk_div));
 	renesas_writel(i3c->regs, MSDVAD, MSDVAD_MDYADV |
author maintainer
#680 lore raw

PATCH 674/713 i3c: renesas: Perform Dynamic Address Assignment on resume

In reply to Greg Kroah-Hartman

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

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

From: Claudiu Beznea <[email protected]>

commit fbf26154c47953d08c60fb402ab19c5c56c3779f upstream.

The Renesas RZ/G3S SoC supports a power saving mode where power to most SoC components, including I3C, is turned off.

On systems where the I3C devices also loses power during suspend (e.g. NXP P3T1085UK-ARD connected to the PMOD1_6A connector of the RZ SMARC Carrier 2 + Renesas RZ/G3S SMARC SOM), the devices becomes unreachable after resume.

Running DAA in the controller resume path restores communication. However, DAA relies on interrupts for TX/RX, which are not available in the noirq suspend/resume phase (unless they are wakeup interrupts). For this, the suspend/resume callbacks were moved out of the noirq phase. Currently, there is no identified use case on either the Renesas RZ/G3S or Renesas RZ/G3E SoCs that requires the controller suspend/resume hooks to be part of the noirq suspend/resume phase.

Since renesas_i3c_reset() is not called anymore in atomic context update it to use read_poll_timeout().

Along with this, struct renesas_i3c::DATBASn and its usage were removed, as they are no longer needed.

---
 drivers/i3c/master/renesas-i3c.c |   38 +++++++++++++++-----------------------
 1 file changed, 15 insertions(+), 23 deletions(-)
drivers/i3c/master/renesas-i3c.c +15 -23
@@ -265,7 +265,6 @@ struct renesas_i3c {
 	u8 addrs[RENESAS_I3C_MAX_DEVS];
 	struct renesas_i3c_xferqueue xferqueue;
 	void __iomem *regs;
-	u32 *DATBASn;
 	struct clk_bulk_data *clks;
 	struct reset_control *presetn;
 	struct reset_control *tresetn;
@@ -495,8 +494,8 @@ static int renesas_i3c_reset(struct rene
 	renesas_writel(i3c->regs, BCTL, 0);
 	renesas_set_bit(i3c->regs, RSTCTL, RSTCTL_RI3CRST);
 
-	return read_poll_timeout_atomic(renesas_readl, val, !(val & RSTCTL_RI3CRST),
-					0, 1000, false, i3c->regs, RSTCTL);
+	return read_poll_timeout(renesas_readl, val, !(val & RSTCTL_RI3CRST),
+				 0, 1000, false, i3c->regs, RSTCTL);
 }
 
 static void renesas_i3c_hw_init(struct renesas_i3c *i3c)
@@ -1425,12 +1424,6 @@ static int renesas_i3c_probe(struct plat
 	i3c->maxdevs = RENESAS_I3C_MAX_DEVS;
 	i3c->free_pos = GENMASK(i3c->maxdevs - 1, 0);
 
-	/* Allocate dynamic Device Address Table backup. */
-	i3c->DATBASn = devm_kzalloc(&pdev->dev, sizeof(u32) * i3c->maxdevs,
-				    GFP_KERNEL);
-	if (!i3c->DATBASn)
-		return -ENOMEM;
-
 	return i3c_master_register(&i3c->base, &pdev->dev, &renesas_i3c_ops, false);
 }
 
@@ -1441,17 +1434,13 @@ static void renesas_i3c_remove(struct pl
 	i3c_master_unregister(&i3c->base);
 }
 
-static int renesas_i3c_suspend_noirq(struct device *dev)
+static int renesas_i3c_suspend(struct device *dev)
 {
 	struct renesas_i3c *i3c = dev_get_drvdata(dev);
-	int i, ret;
+	int ret;
 
 	i2c_mark_adapter_suspended(&i3c->base.i2c);
 
-	/* Store Device Address Table values. */
-	for (i = 0; i < i3c->maxdevs; i++)
-		i3c->DATBASn[i] = renesas_readl(i3c->regs, DATBAS(i));
-
 	ret = reset_control_assert(i3c->presetn);
 	if (ret)
 		goto err_mark_resumed;
@@ -1472,10 +1461,10 @@ err_mark_resumed:
 	return ret;
 }
 
-static int renesas_i3c_resume_noirq(struct device *dev)
+static int renesas_i3c_resume(struct device *dev)
 {
 	struct renesas_i3c *i3c = dev_get_drvdata(dev);
-	int i, ret;
+	int ret;
 
 	ret = reset_control_deassert(i3c->tresetn);
 	if (ret)
@@ -1501,15 +1490,19 @@ static int renesas_i3c_resume_noirq(stru
 	renesas_writel(i3c->regs, MSDVAD, MSDVAD_MDYADV |
 		       MSDVAD_MDYAD(i3c->dyn_addr));
 
-	/* Restore Device Address Table values. */
-	for (i = 0; i < i3c->maxdevs; i++)
-		renesas_writel(i3c->regs, DATBAS(i), i3c->DATBASn[i]);
-
 	/* I3C hw init. */
 	renesas_i3c_hw_init(i3c);
 
+	ret = i3c_master_do_daa_ext(&i3c->base, true);
+	if (ret)
+		dev_err(dev, "DAA failed on resume, ret=%d", ret);
+
 	i2c_mark_adapter_resumed(&i3c->base.i2c);
 
+	/*
+	 * I3C devices may have retained their dynamic address anyway. Do not
+	 * fail the resume because of DAA error.
+	 */
 	return 0;
 
 err_clks_disable:
@@ -1522,8 +1515,7 @@ err_tresetn:
 }
 
 static const struct dev_pm_ops renesas_i3c_pm_ops = {
-	NOIRQ_SYSTEM_SLEEP_PM_OPS(renesas_i3c_suspend_noirq,
-				  renesas_i3c_resume_noirq)
+	SYSTEM_SLEEP_PM_OPS(renesas_i3c_suspend, renesas_i3c_resume)
 };
 
 static const struct of_device_id renesas_i3c_of_ids[] = {
author maintainer
#681 lore raw

PATCH 675/713 wifi: brcmfmac: Fix memory leak in brcmf_sdio_read_control()

In reply to Greg Kroah-Hartman

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

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

From: Abdun Nihaal <[email protected]>

commit 0d10db8e94fcb23a799789aaa696b4d8f937e207 upstream.

The memory allocated for buf is not freed in some of the error paths in brcmf_sdio_read_control(). Fix that by adding vfree() calls.

Fixes dd43a01c5cdb ("brcmfmac: use dynamically allocated control frame buffer")
[arend: rework as suggested by Johannes]
---
 drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c |    3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c +2 -1
@@ -1827,17 +1827,18 @@ gotpkt:
 	if (bus->rxctl) {
 		brcmf_err("last control frame is being processed.\n");
 		spin_unlock_bh(&bus->rxctl_lock);
-		vfree(buf);
 		goto done;
 	}
 	bus->rxctl = buf + doff;
 	bus->rxctl_orig = buf;
 	bus->rxlen = len - doff;
 	spin_unlock_bh(&bus->rxctl_lock);
+	buf = NULL;
 
 done:
 	/* Awake any waiters */
 	brcmf_sdio_dcmd_resp_wake(bus);
+	vfree(buf);
 }
 
 /* Pad read to blocksize for efficiency */
author maintainer
#682 lore raw

PATCH 676/713 wifi: iwlwifi: dvm: fix memory leak in iwl_op_mode_dvm_start()

In reply to Greg Kroah-Hartman

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

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

From: Dawei Feng <[email protected]>

commit 67105abd6195a685a84dcb8a5daf54a1f4bfdb60 upstream.

In iwl_op_mode_dvm_start(), jumping to out_free_eeprom currently bypasses the out_free_eeprom_blob label. Consequently, error paths triggered after successfully parsing the EEPROM free priv->nvm_data but leak priv->eeprom_blob.

Fix this memory leak by reordering the error handling labels so that out_free_eeprom falls through to out_free_eeprom_blob.

The bug was first flagged by an experimental analysis tool we are developing for kernel memory-management bugs while analyzing v6.13-rc1. The tool is still under development and is not yet publicly available. Manual inspection confirms that the bug is still present in v7.1-rc6.

An x86_64 allyesconfig build showed no new warnings. As we do not have supported Intel DVM wireless hardware and firmware to test with, no runtime testing was able to be performed.

---
 drivers/net/wireless/intel/iwlwifi/dvm/main.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/net/wireless/intel/iwlwifi/dvm/main.c +2 -2
@@ -1511,10 +1511,10 @@ out_destroy_workqueue:
 	priv->workqueue = NULL;
 out_uninit_drv:
 	iwl_uninit_drv(priv);
-out_free_eeprom_blob:
-	kfree(priv->eeprom_blob);
 out_free_eeprom:
 	kfree(priv->nvm_data);
+out_free_eeprom_blob:
+	kfree(priv->eeprom_blob);
 out_leave_trans:
 	iwl_trans_op_mode_leave(priv->trans);
 out_free_hw:
author maintainer
#683 lore raw

PATCH 677/713 wifi: rtl8xxxu: fix use-after-free from rx_urb_wq on stop

In reply to Greg Kroah-Hartman

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

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

From: Fan Wu <[email protected]>

commit 6c080026ecc17eecb103f8927c64ea73a74bb818 upstream.

rtl8xxxu arms rx_urb_wq from the RX completion path:
rtl8xxxu_rx_complete() hands the URB to rtl8xxxu_queue_rx_urb(), which queues it on rx_urb_pending_list and, once the list grows past RTL8XXXU_RX_URB_PENDING_WATER, schedules rx_urb_wq. The worker rtl8xxxu_rx_urb_work() drains rx_urb_pending_list, recovers priv through container_of, and resubmits each URB through rtl8xxxu_submit_rx_urb(), which anchors it on rx_anchor and dereferences priv->udev.

rtl8xxxu_stop() cancels the sibling work items (c2hcmd_work, ra_watchdog, update_beacon_work) but never cancels rx_urb_wq, so a worker armed during the last burst of RX traffic can run rtl8xxxu_rx_urb_work() after rtl8xxxu_disconnect() has called ieee80211_free_hw(), which frees priv, producing a use-after-free. The window opens under active RX traffic (pending count above the watermark) followed by a disconnect.

There are two teardown races to close:

* rtl8xxxu_queue_rx_urb() decided whether to enqueue under rx_urb_lock but called schedule_work() after dropping the lock. A completion that observed shutdown == false and released the lock could then call schedule_work() after rtl8xxxu_stop() had set shutdown and cancel_work_sync() had already returned, arming the worker to run after the teardown. Move schedule_work() under the same !shutdown branch so the arming decision is atomic with the shutdown check.

* rtl8xxxu_rx_urb_work() anchors every URB it drained back onto rx_anchor through rtl8xxxu_submit_rx_urb(). A worker still running when usb_kill_anchored_urbs(&priv->rx_anchor) returned would submit a URB that escaped the kill. In rtl8xxxu_stop(), call cancel_work_sync(&priv->rx_urb_wq) before the kill so the worker is drained first.

After priv->shutdown is set under rx_urb_lock, completions can no longer queue rx_urb_wq. cancel_work_sync() then drains the last queued or running worker, and the following usb_kill_anchored_urbs() kills the URBs it may have submitted.

rtl8xxxu_disconnect() is covered because ieee80211_unregister_hw() guarantees .stop() runs for a live interface before ieee80211_free_hw() frees priv. The probe error path needs no cancel: rx_urb_wq is INIT_WORK()'d there but cannot have been scheduled, since no URB is submitted before ieee80211_register_hw() succeeds.

This bug was found by static analysis.

---
 drivers/net/wireless/realtek/rtl8xxxu/core.c |   19 ++++++++++++++-----
 1 file changed, 14 insertions(+), 5 deletions(-)
drivers/net/wireless/realtek/rtl8xxxu/core.c +14 -5
@@ -5838,14 +5838,19 @@ static void rtl8xxxu_queue_rx_urb(struct
 {
 	struct sk_buff *skb;
 	unsigned long flags;
-	int pending = 0;
 
 	spin_lock_irqsave(&priv->rx_urb_lock, flags);
 
 	if (!priv->shutdown) {
 		list_add_tail(&rx_urb->list, &priv->rx_urb_pending_list);
 		priv->rx_urb_pending_count++;
-		pending = priv->rx_urb_pending_count;
+		/*
+		 * Arm the worker under rx_urb_lock so this is atomic with the
+		 * shutdown check: moving it out of the lock would let a
+		 * completion arm the work after rtl8xxxu_stop() canceled it.
+		 */
+		if (priv->rx_urb_pending_count > RTL8XXXU_RX_URB_PENDING_WATER)
+			schedule_work(&priv->rx_urb_wq);
 	} else {
 		skb = (struct sk_buff *)rx_urb->urb.context;
 		dev_kfree_skb_irq(skb);
@@ -5853,9 +5858,6 @@ static void rtl8xxxu_queue_rx_urb(struct
 	}
 
 	spin_unlock_irqrestore(&priv->rx_urb_lock, flags);
-
-	if (pending > RTL8XXXU_RX_URB_PENDING_WATER)
-		schedule_work(&priv->rx_urb_wq);
 }
 
 static void rtl8xxxu_rx_urb_work(struct work_struct *work)
@@ -7507,6 +7509,13 @@ static void rtl8xxxu_stop(struct ieee802
 	priv->shutdown = true;
 	spin_unlock_irqrestore(&priv->rx_urb_lock, flags);
 
+	/*
+	 * Cancel before killing rx_anchor: the worker re-anchors every URB
+	 * it drained via rtl8xxxu_submit_rx_urb(), so a worker still running
+	 * after the kill could submit a URB that escapes it.
+	 */
+	cancel_work_sync(&priv->rx_urb_wq);
+
 	usb_kill_anchored_urbs(&priv->rx_anchor);
 	usb_kill_anchored_urbs(&priv->tx_anchor);
 	if (priv->usb_interrupts)
author maintainer
#684 lore raw

PATCH 678/713 fuse: decouple fuse_ring creation from ent registration

In reply to Greg Kroah-Hartman

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

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

From: Joanne Koong <[email protected]>

[ Upstream commit 6330b1f61ed1d17850fc61bdb8920ca1056e2cf9 ]

Currently, the connection's fuse_ring is created lazily on the first FUSE_IO_URING_CMD_REGISTER command. A server registers entries from one thread per queue (one per CPU) and those threads issue their first REGISTER command concurrently. They then race to create the single per-connection fuse_ring, which required open-coded handling in fuse_uring_create() to detect and protect against concurrent creations.

Decouple fuse_ring creation from ent registration and move it to FUSE_INIT reply processing after a server has negotiated and set FUSE_OVER_IO_URING. The ring is published before the connection is marked initialized. fuse_uring_register() no longer creates the ring and it instead uses the ring set up at init time.

Stable-dep-of: fd10f40af314 ("fuse: copy request headers via a stack buffer for io-uring")

---
 fs/fuse/dev.c         |    8 +++-----
 fs/fuse/dev.h         |    2 +-
 fs/fuse/dev_uring.c   |   26 ++++++++++----------------
 fs/fuse/dev_uring_i.h |    5 +++++
 fs/fuse/inode.c       |    4 +++-
 5 files changed, 22 insertions(+), 23 deletions(-)
fs/fuse/dev.c +3 -5
@@ -75,6 +75,9 @@ void fuse_chan_set_initialized(struct fu
 		fch->minor = param->minor;
 		fch->max_write = param->max_write;
 		fch->max_pages = param->max_pages;
+
+		if (param->io_uring_enabled)
+			fuse_uring_conn_init(fch);
 	}
 
 	/* Make sure stores before this are seen on another CPU */
@@ -415,11 +418,6 @@ void fuse_chan_set_fc(struct fuse_chan *
 	fch->conn = fc;
 }
 
-void fuse_chan_io_uring_enable(struct fuse_chan *fch)
-{
-	fch->io_uring = 1;
-}
-
 void fuse_pqueue_init(struct fuse_pqueue *fpq)
 {
 	spin_lock_init(&fpq->lock);
fs/fuse/dev.h +1 -1
@@ -22,6 +22,7 @@ struct fuse_chan_param {
 	unsigned int minor;
 	unsigned int max_write;
 	unsigned int max_pages;
+	bool io_uring_enabled;
 };
 
 struct fuse_chan *fuse_chan_new(void);
@@ -34,7 +35,6 @@ void fuse_chan_max_background_set(struct
 unsigned int fuse_chan_num_waiting(struct fuse_chan *fch);
 void fuse_chan_set_fc(struct fuse_chan *fch, struct fuse_conn *fc);
 void fuse_chan_set_initialized(struct fuse_chan *fch, struct fuse_chan_param *param);
-void fuse_chan_io_uring_enable(struct fuse_chan *fch);
 ssize_t fuse_chan_send(struct fuse_chan *fch, struct fuse_args *args);
 int fuse_chan_send_bg(struct fuse_chan *fch, struct fuse_args *args, gfp_t gfp_flags);
 int fuse_chan_send_notify_reply(struct fuse_chan *fch, struct fuse_args *args, u64 unique);
fs/fuse/dev_uring.c +10 -16
@@ -238,7 +238,6 @@ static struct fuse_ring *fuse_uring_crea
 {
 	struct fuse_ring *ring;
 	size_t nr_queues = num_possible_cpus();
-	struct fuse_ring *res = NULL;
 	size_t max_payload_size;
 
 	ring = kzalloc_obj(*ring, GFP_KERNEL_ACCOUNT);
@@ -258,12 +257,6 @@ static struct fuse_ring *fuse_uring_crea
 		spin_unlock(&fch->lock);
 		goto out_err;
 	}
-	if (fch->ring) {
-		/* race, another thread created the ring in the meantime */
-		spin_unlock(&fch->lock);
-		res = fch->ring;
-		goto out_err;
-	}
 
 	init_waitqueue_head(&ring->stop_waitq);
 
@@ -278,7 +271,13 @@ static struct fuse_ring *fuse_uring_crea
 out_err:
 	kfree(ring->queues);
 	kfree(ring);
-	return res;
+	return NULL;
+}
+
+void fuse_uring_conn_init(struct fuse_chan *fch)
+{
+	if (fuse_uring_create(fch))
+		fch->io_uring = 1;
 }
 
 static struct fuse_ring_queue *fuse_uring_create_queue(struct fuse_ring *ring,
@@ -1178,15 +1177,10 @@ static int fuse_uring_register(struct io
 	struct fuse_ring *ring = smp_load_acquire(&fch->ring);
 	struct fuse_ring_queue *queue;
 	struct fuse_ring_ent *ent;
-	int err;
 	unsigned int qid = READ_ONCE(cmd_req->qid);
 
-	err = -ENOMEM;
-	if (!ring) {
-		ring = fuse_uring_create(fch);
-		if (!ring)
-			return err;
-	}
+	if (!ring)
+		return -EINVAL;
 
 	if (qid >= ring->nr_queues) {
 		pr_info_ratelimited("fuse: Invalid ring qid %u\n", qid);
@@ -1197,7 +1191,7 @@ static int fuse_uring_register(struct io
 	if (!queue) {
 		queue = fuse_uring_create_queue(ring, qid);
 		if (!queue)
-			return err;
+			return -ENOMEM;
 	}
 
 	/*
fs/fuse/dev_uring_i.h +5 -0
@@ -135,6 +135,7 @@ struct fuse_ring {
 	bool ready;
 };
 
+void fuse_uring_conn_init(struct fuse_chan *fch);
 void fuse_uring_stop_queues(struct fuse_ring *ring);
 void fuse_uring_abort_end_requests(struct fuse_ring *ring);
 int fuse_uring_cmd(struct io_uring_cmd *cmd, unsigned int issue_flags);
@@ -174,6 +175,10 @@ static inline bool fuse_uring_ready(stru
 
 #else /* CONFIG_FUSE_IO_URING */
 
+static inline void fuse_uring_conn_init(struct fuse_chan *fch)
+{
+}
+
 static inline void fuse_uring_abort(struct fuse_chan *fch)
 {
 }
fs/fuse/inode.c +3 -1
@@ -1272,6 +1272,7 @@ static void process_init_reply(struct fu
 	struct fuse_mount *fm = ia->fm;
 	struct fuse_conn *fc = fm->fc;
 	struct fuse_init_out *arg = &ia->out;
+	bool io_uring_enabled = false;
 	bool ok = true;
 
 	if (error || arg->major != FUSE_KERNEL_VERSION)
@@ -1402,7 +1403,7 @@ static void process_init_reply(struct fu
 					ok = false;
 			}
 			if (flags & FUSE_OVER_IO_URING && fuse_uring_enabled())
-				fuse_chan_io_uring_enable(fc->chan);
+				io_uring_enabled = true;
 
 			if (flags & FUSE_REQUEST_TIMEOUT)
 				timeout = arg->request_timeout;
@@ -1432,6 +1433,7 @@ static void process_init_reply(struct fu
 			.minor = fc->minor,
 			.max_write = fc->max_write,
 			.max_pages = fc->max_pages,
+			.io_uring_enabled = io_uring_enabled,
 		};
 		fuse_chan_set_initialized(fc->chan, &cp);
 	}
author maintainer
#685 lore raw

PATCH 679/713 fuse: copy request headers via a stack buffer for io-uring

In reply to Greg Kroah-Hartman

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

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

From: Xiang Mei <[email protected]>

[ Upstream commit fd10f40af314f07b6d6e028b1ca25c8b49903aab ]

The fuse-io-uring transport copies req->in.h out to the ring in fuse_uring_copy_to_ring() and req->out.h back in fuse_uring_commit(). Both headers live inside the fuse_request slab object, whose cache (fuse_req_cachep) is created without a usercopy whitelist, so copying them directly to/from userspace trips CONFIG_HARDENED_USERCOPY and panics:

usercopy: Kernel memory exposure attempt detected from SLUB object 'fuse_request' (offset 56, size 40)!
kernel BUG at mm/usercopy.c:102!

  Oops: invalid opcode: 0000 [#1] SMP KASAN NOPTI
  RIP: 0010:usercopy_abort (mm/usercopy.c:90)
  Call Trace:
   __check_heap_object (mm/slub.c:8268)
   __check_object_size (mm/usercopy.c:197 mm/usercopy.c:258 mm/usercopy.c:223)
   copy_header_to_ring (fs/fuse/dev_uring.c:618)
   fuse_uring_prepare_send (fs/fuse/dev_uring.c:776 fs/fuse/dev_uring.c:785)
   fuse_uring_send_in_task (fs/fuse/dev_uring.c:1306)
   tctx_task_work_run (io_uring/tw.c:96)
   task_work_run (kernel/task_work.c:233)
   io_run_task_work (io_uring/tw.h:84)
   io_cqring_wait (io_uring/wait.c:278)
   __do_sys_io_uring_enter (io_uring/io_uring.c:2685)
   entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121)

Bounce both headers through an on-stack copy so the usercopy touches stack memory, not the slab object.

Fixes c090c8abae4b ("fuse: Add io-uring sqe commit and fetch support")
Assisted-by Claude:claude-opus-4-8
[ adapted context due to a pre-existing local `ring` local variable from an earlier already-backported commit shifting line numbers ]
---
 fs/fuse/dev_uring.c |   12 ++++++++----
 1 file changed, 8 insertions(+), 4 deletions(-)
fs/fuse/dev_uring.c +8 -4
@@ -745,6 +745,7 @@ static int fuse_uring_copy_to_ring(struc
 {
 	struct fuse_ring_queue *queue = ent->queue;
 	struct fuse_ring *ring = queue->ring;
+	struct fuse_in_header in_header;
 	int err;
 
 	err = -EIO;
@@ -766,8 +767,9 @@ static int fuse_uring_copy_to_ring(struc
 	}
 
 	/* copy fuse_in_header */
-	return copy_header_to_ring(ent, FUSE_URING_HEADER_IN_OUT, &req->in.h,
-				   sizeof(req->in.h));
+	in_header = req->in.h;
+	return copy_header_to_ring(ent, FUSE_URING_HEADER_IN_OUT, &in_header,
+				   sizeof(in_header));
 }
 
 static int fuse_uring_prepare_send(struct fuse_ring_ent *ent,
@@ -872,11 +874,13 @@ static void fuse_uring_commit(struct fus
 			      unsigned int issue_flags)
 {
 	struct fuse_ring *ring = ent->queue->ring;
+	struct fuse_out_header out_header;
 	ssize_t err = -EFAULT;
 
-	if (copy_header_from_ring(ent, FUSE_URING_HEADER_IN_OUT, &req->out.h,
-				  sizeof(req->out.h)))
+	if (copy_header_from_ring(ent, FUSE_URING_HEADER_IN_OUT, &out_header,
+				  sizeof(out_header)))
 		goto out;
+	req->out.h = out_header;
 
 	err = fuse_uring_out_header_has_err(&req->out.h, req);
 	if (err) {
author maintainer
#686 lore raw

PATCH 680/713 crypto: iaa - unmap dst before software fallback on decompress

In reply to Greg Kroah-Hartman

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

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

From: Vinicius Costa Gomes <[email protected]>

[ Upstream commit 94a25930477113730372e0fa2985da4c5ac95c9a ]

On a hardware analytics error, decompress retries through the software fallback, which writes req->dst with the CPU while it is still mapped DMA_FROM_DEVICE. With SWIOTLB active the later dma_unmap_sg() copies the stale bounce buffer over req->dst, corrupting the result.

Unmap before the fallback runs. The async path unmaps inline; the sync path signals the retry with -EAGAIN so iaa_comp_adecompress() runs the fallback after unmapping.

Fixes 2ec6761df889 ("crypto: iaa - Add support for deflate-iaa compression algorithm")
[ adapted unavailable iaa_unmap_src() calls to single-entry dma_unmap_sg() calls ]
---
 drivers/crypto/intel/iaa/iaa_crypto_main.c |   27 ++++++++++++---------------
 1 file changed, 12 insertions(+), 15 deletions(-)
drivers/crypto/intel/iaa/iaa_crypto_main.c +12 -15
@@ -1058,13 +1058,17 @@ static void iaa_desc_complete(struct idx
 			pr_warn("%s: falling back to deflate-generic decompress, "
 				"analytics error code %x\n", __func__,
 				idxd_desc->iax_completion->error_code);
+			dma_unmap_sg(dev, ctx->req->dst, sg_nents(ctx->req->dst),
+				     DMA_FROM_DEVICE);
+			dma_unmap_sg(dev, ctx->req->src, 1, DMA_TO_DEVICE);
+
 			ret = deflate_generic_decompress(ctx->req);
 			if (ret) {
 				dev_dbg(dev, "%s: deflate-generic failed ret=%d\n",
 					__func__, ret);
 				err = -EIO;
-				goto err;
 			}
+			goto out;
 		} else {
 			err = -EIO;
 			goto err;
@@ -1447,19 +1451,9 @@ static int iaa_decompress(struct crypto_
 	ret = check_completion(dev, idxd_desc->iax_completion, false, false);
 	if (ret) {
 		dev_dbg(dev, "%s: check_completion failed ret=%d\n", __func__, ret);
-		if (idxd_desc->iax_completion->status == IAA_ANALYTICS_ERROR) {
-			pr_warn("%s: falling back to deflate-generic decompress, "
-				"analytics error code %x\n", __func__,
-				idxd_desc->iax_completion->error_code);
-			ret = deflate_generic_decompress(req);
-			if (ret) {
-				dev_dbg(dev, "%s: deflate-generic failed ret=%d\n",
-					__func__, ret);
-				goto err;
-			}
-		} else {
-			goto err;
-		}
+		if (idxd_desc->iax_completion->status == IAA_ANALYTICS_ERROR)
+			ret = -EAGAIN;
+		goto err;
 	} else {
 		req->dlen = idxd_desc->iax_completion->output_size;
 	}
@@ -1647,13 +1641,16 @@ static int iaa_comp_adecompress(struct a
 	if (ret == -EINPROGRESS)
 		return ret;
 
-	if (ret != 0)
+	if (ret != 0 && ret != -EAGAIN)
 		dev_dbg(dev, "asynchronous decompress failed ret=%d\n", ret);
 
 	dma_unmap_sg(dev, req->dst, 1, DMA_FROM_DEVICE);
 	dma_unmap_sg(dev, req->src, 1, DMA_TO_DEVICE);
 	iaa_wq_put(wq);
 
+	if (ret == -EAGAIN)
+		ret = deflate_generic_decompress(req);
+
 	return ret;
 }
 
author maintainer
#687 lore raw

PATCH 681/713 crypto: atmel-ecc - clean up and improve ECDH comments

In reply to Greg Kroah-Hartman

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

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

From: Thorsten Blum <[email protected]>

[ Upstream commit 3e84fb698abada239d3e35ed3d52a24dbfda5f6a ]

Improve the kerneldoc for struct atmel_ecdh_ctx by removing the stale "unsupported curves" wording, since the device only supports a single curve (P-256), and move the set_secret() constraint to the description.

In atmel_ecdh_set_secret(), clarify that the device generates the private key, and drop the redundant "only supports NIST P256" comment.

In atmel_ecdh_done() and atmel_ecdh_generate_public_key(), clarify the truncation comments. Also note that a P-256 public key consists of two 32-byte coordinates in atmel_ecdh_compute_shared_secret(), and remove the unnecessary fall-through comment and other redundant comments.

Stable-dep-of: f07a0d251db7 ("crypto: atmel-ecc - avoid stale fallback key after set_secret failure")

---
 drivers/crypto/atmel-ecc.c |   38 ++++++++++++++------------------------
 1 file changed, 14 insertions(+), 24 deletions(-)
drivers/crypto/atmel-ecc.c +14 -24
@@ -27,15 +27,14 @@ static struct atmel_ecc_driver_data driv
 
 /**
  * struct atmel_ecdh_ctx - transformation context
- * @client     : pointer to i2c client device
- * @fallback   : used for unsupported curves or when user wants to use its own
- *               private key.
- * @public_key : generated when calling set_secret(). It's the responsibility
- *               of the user to not call set_secret() while
- *               generate_public_key() or compute_shared_secret() are in flight.
- * @curve_id   : elliptic curve id
- * @do_fallback: true when the device doesn't support the curve or when the user
- *               wants to use its own private key.
+ * @client: I2C client device
+ * @fallback: ECDH fallback used for caller-provided private keys
+ * @public_key: cached public key for the device-generated private key
+ * @curve_id: elliptic curve id
+ * @do_fallback: true when ECDH operations should use @fallback
+ *
+ * The caller must not invoke set_secret() while generate_public_key()
+ * or compute_shared_secret() are in flight.
  */
 struct atmel_ecdh_ctx {
 	struct i2c_client *client;
@@ -55,7 +54,7 @@ static void atmel_ecdh_done(struct atmel
 	if (status)
 		goto free_work_data;
 
-	/* might want less than we've got */
+	/* copy only as much as requested, capped at 32 bytes */
 	n_sz = min(ATMEL_ECC_NIST_P256_N_SIZE, req->dst_len);
 
 	/* copy the shared secret */
@@ -64,15 +63,15 @@ static void atmel_ecdh_done(struct atmel
 	if (copied != n_sz)
 		status = -EINVAL;
 
-	/* fall through */
 free_work_data:
 	kfree_sensitive(work_data);
 	kpp_request_complete(req, status);
 }
 
 /*
- * A random private key is generated and stored in the device. The device
- * returns the pair public key.
+ * If no private key is provided, generate one in the device and cache
+ * the corresponding public key. The generated private key never leaves
+ * the device.
  */
 static int atmel_ecdh_set_secret(struct crypto_kpp *tfm, const void *buf,
 				 unsigned int len)
@@ -83,9 +82,7 @@ static int atmel_ecdh_set_secret(struct
 	struct ecdh params;
 	int ret = -ENOMEM;
 
-	/* free the old public key, if any */
 	kfree(ctx->public_key);
-	/* make sure you don't free the old public key twice */
 	ctx->public_key = NULL;
 
 	if (crypto_ecdh_decode_key(buf, len, &params) < 0) {
@@ -94,7 +91,6 @@ static int atmel_ecdh_set_secret(struct
 	}
 
 	if (params.key_size) {
-		/* fallback to ecdh software implementation */
 		ctx->do_fallback = true;
 		return crypto_kpp_set_secret(ctx->fallback, buf, len);
 	}
@@ -103,11 +99,6 @@ static int atmel_ecdh_set_secret(struct
 	if (!cmd)
 		return -ENOMEM;
 
-	/*
-	 * The device only supports NIST P256 ECC keys. The public key size will
-	 * always be the same. Use a macro for the key size to avoid unnecessary
-	 * computations.
-	 */
 	public_key = kmalloc(ATMEL_ECC_PUBKEY_SIZE, GFP_KERNEL);
 	if (!public_key)
 		goto free_cmd;
@@ -120,7 +111,6 @@ static int atmel_ecdh_set_secret(struct
 	if (ret)
 		goto free_public_key;
 
-	/* save the public key */
 	memcpy(public_key, &cmd->data[RSP_DATA_IDX], ATMEL_ECC_PUBKEY_SIZE);
 	ctx->public_key = public_key;
 
@@ -149,7 +139,7 @@ static int atmel_ecdh_generate_public_ke
 	if (!ctx->public_key)
 		return -EINVAL;
 
-	/* might want less than we've got */
+	/* copy only as much as requested, capped at 64 bytes */
 	nbytes = min(ATMEL_ECC_PUBKEY_SIZE, req->dst_len);
 
 	/* public key was saved at private key generation */
@@ -175,7 +165,7 @@ static int atmel_ecdh_compute_shared_sec
 		return crypto_kpp_compute_shared_secret(req);
 	}
 
-	/* must have exactly two points to be on the curve */
+	/* A P-256 public key must contain two 32-byte coordinates */
 	if (req->src_len != ATMEL_ECC_PUBKEY_SIZE)
 		return -EINVAL;
 
author maintainer
#688 lore raw

PATCH 682/713 crypto: atmel-ecc - avoid stale fallback key after set_secret failure

In reply to Greg Kroah-Hartman

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

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

From: Thorsten Blum <[email protected]>

[ Upstream commit f07a0d251db7606e4792d2610788fbcc7b2c0d12 ]

Clear ->do_fallback before decoding a new ECDH secret and enable it only after the software fallback accepts a caller-provided private key. This avoids using a stale fallback key should crypto_kpp_set_secret() fail.

Fixes 11105693fa05 ("crypto: atmel-ecc - introduce Microchip / Atmel ECC driver")
---
 drivers/crypto/atmel-ecc.c |    8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)
drivers/crypto/atmel-ecc.c +4 -4
@@ -84,6 +84,7 @@ static int atmel_ecdh_set_secret(struct
 
 	kfree(ctx->public_key);
 	ctx->public_key = NULL;
+	ctx->do_fallback = false;
 
 	if (crypto_ecdh_decode_key(buf, len, &params) < 0) {
 		dev_err(&ctx->client->dev, "crypto_ecdh_decode_key failed\n");
@@ -91,8 +92,9 @@ static int atmel_ecdh_set_secret(struct
 	}
 
 	if (params.key_size) {
-		ctx->do_fallback = true;
-		return crypto_kpp_set_secret(ctx->fallback, buf, len);
+		ret = crypto_kpp_set_secret(ctx->fallback, buf, len);
+		ctx->do_fallback = !ret;
+		return ret;
 	}
 
 	cmd = kmalloc_obj(*cmd);
@@ -103,8 +105,6 @@ static int atmel_ecdh_set_secret(struct
 	if (!public_key)
 		goto free_cmd;
 
-	ctx->do_fallback = false;
-
 	atmel_i2c_init_genkey_cmd(cmd, DATA_SLOT_2);
 
 	ret = atmel_i2c_send_receive(ctx->client, cmd);
author maintainer
#689 lore raw

PATCH 683/713 mm/kmemleak: stop the task stack scan early when interrupted

In reply to Greg Kroah-Hartman

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

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

From: Breno Leitao <[email protected]>

[ Upstream commit eb11f56eeca56069613d00bf623607e305b271d9 ]

scan_block() already checks scan_should_stop() for every pointer and bails out of the current block, but the task stack walk cannot tell and keeps issuing a separate scan_should_stop() between every task.

Return that status from scan_block() and use it as the task stack loop condition, so the walk stops as soon as a scan is interrupted.

Stable-dep-of: 3541a2b06ecd ("mm/kmemleak: report RCU-tasks quiescent states during the scan")

---
 mm/kmemleak.c |   18 ++++++++++++------
 1 file changed, 12 insertions(+), 6 deletions(-)
mm/kmemleak.c +12 -6
@@ -1525,22 +1525,25 @@ static int scan_should_stop(void)
 
 /*
  * Scan a memory block (exclusive range) for valid pointers and add those
- * found to the gray list.
+ * found to the gray list. Return non-zero if the scan was interrupted.
  */
-static void scan_block(void *_start, void *_end,
-		       struct kmemleak_object *scanned)
+static int scan_block(void *_start, void *_end,
+		      struct kmemleak_object *scanned)
 {
 	unsigned long *ptr;
 	unsigned long *start = PTR_ALIGN(_start, BYTES_PER_POINTER);
 	unsigned long *end = _end - (BYTES_PER_POINTER - 1);
 	unsigned long flags;
+	int stop = 0;
 
 	raw_spin_lock_irqsave(&kmemleak_lock, flags);
 	for (ptr = start; ptr < end; ptr++) {
 		unsigned long pointer;
 
-		if (scan_should_stop())
+		if (scan_should_stop()) {
+			stop = 1;
 			break;
+		}
 
 		kasan_disable_current();
 		pointer = *(unsigned long *)kasan_reset_tag((void *)ptr);
@@ -1550,6 +1553,8 @@ static void scan_block(void *_start, voi
 		pointer_update_refs(scanned, pointer, OBJECT_PERCPU);
 	}
 	raw_spin_unlock_irqrestore(&kmemleak_lock, flags);
+
+	return stop;
 }
 
 /*
@@ -1705,6 +1710,7 @@ static void kmemleak_scan_task_stacks(vo
 {
 	struct pid *pid;
 	int nr = 1;
+	int stop = 0;
 
 	do {
 		struct task_struct *p = NULL;
@@ -1723,13 +1729,13 @@ static void kmemleak_scan_task_stacks(vo
 			void *stack = try_get_task_stack(p);
 
 			if (stack) {
-				scan_block(stack, stack + THREAD_SIZE, NULL);
+				stop = scan_block(stack, stack + THREAD_SIZE, NULL);
 				put_task_stack(p);
 			}
 			put_task_struct(p);
 		}
 		cond_resched();
-	} while (pid && !scan_should_stop());
+	} while (pid && !stop);
 }
 
 /*
author maintainer
#690 lore raw

PATCH 684/713 mm/kmemleak: report RCU-tasks quiescent states during the scan

In reply to Greg Kroah-Hartman

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

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

From: Breno Leitao <[email protected]>

[ Upstream commit 3541a2b06ecd78ba333188df04368dcf97273d6a ]

kmemleak_scan() can run for ages on large debug kernels. It was causing some soft-lockups which I got fixed with commit 3175fcfec8b16baeb ("mm/kmemleak: avoid soft lockup when scanning task stacks") with our beloved cond_resched().

I've got the fix above deployed in the Meta fleet, and now I am seeing:

  INFO: rcu_tasks detected stalls on tasks:
    task:kmemleak state:R ... nvcsw: 274/274 holdout: 1 idle_cpu: -1/3
     scan_block
     scan_gray_list
     kmemleak_scan

and, worse, blocks the callers waiting on that grace period. Here a BPF struct_ops map free, which waits via synchronize_rcu_mult(call_rcu, call_rcu_tasks), is stuck long enough to also trip the hung task check:

  INFO: task kworker/...:bpf_map_free_deferred blocked for 122 seconds
     __wait_rcu_gp
     bpf_struct_ops_map_free

Then I've learned that cond_resched() is not an RCU-tasks quiescent state, so, we need to use stronger primitives.

Use cond_resched_tasks_rcu_qs() at the scan reschedule points so the scan reports an RCU-tasks quiescent state as it proceeds.

Inspired by commit b96285e10aad ("tracing: Have osnoise_main() add a quiescent state for task rcu").

Fixes c4b28963fd79 ("mm/kmemleak: rely on rcu for task stack scanning")
Cc Breno Leitao <[email protected]>
Cc Puranjay Mohan <[email protected]>
[ adapted scan_large_block() to the branch’s existing void implementation without interruption propagation. ]
---
 mm/kmemleak.c |   14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)
mm/kmemleak.c +7 -7
@@ -1569,7 +1569,7 @@ static void scan_large_block(void *start
 		next = min(start + MAX_SCAN_SIZE, end);
 		scan_block(start, next, NULL);
 		start = next;
-		cond_resched();
+		cond_resched_tasks_rcu_qs();
 	}
 }
 #endif
@@ -1604,7 +1604,7 @@ static void scan_object(struct kmemleak_
 			scan_block(start, end, object);
 
 			raw_spin_unlock_irqrestore(&object->lock, flags);
-			cond_resched();
+			cond_resched_tasks_rcu_qs();
 			raw_spin_lock_irqsave(&object->lock, flags);
 			if (!(object->flags & OBJECT_ALLOCATED))
 				break;
@@ -1626,7 +1626,7 @@ static void scan_object(struct kmemleak_
 				break;
 
 			raw_spin_unlock_irqrestore(&object->lock, flags);
-			cond_resched();
+			cond_resched_tasks_rcu_qs();
 			raw_spin_lock_irqsave(&object->lock, flags);
 		} while (object->flags & OBJECT_ALLOCATED);
 	} else {
@@ -1654,7 +1654,7 @@ static void scan_gray_list(void)
 	 */
 	object = list_entry(gray_list.next, typeof(*object), gray_list);
 	while (&object->gray_list != &gray_list) {
-		cond_resched();
+		cond_resched_tasks_rcu_qs();
 
 		/* may add new objects to the list */
 		if (!scan_should_stop())
@@ -1689,7 +1689,7 @@ static void kmemleak_cond_resched(struct
 	raw_spin_unlock_irq(&kmemleak_lock);
 
 	rcu_read_unlock();
-	cond_resched();
+	cond_resched_tasks_rcu_qs();
 	rcu_read_lock();
 
 	raw_spin_lock_irq(&kmemleak_lock);
@@ -1734,7 +1734,7 @@ static void kmemleak_scan_task_stacks(vo
 			}
 			put_task_struct(p);
 		}
-		cond_resched();
+		cond_resched_tasks_rcu_qs();
 	} while (pid && !stop);
 }
 
@@ -1908,7 +1908,7 @@ static void kmemleak_scan(void)
 			struct page *page = pfn_to_online_page(pfn);
 
 			if (!(pfn & 63))
-				cond_resched();
+				cond_resched_tasks_rcu_qs();
 
 			if (!page)
 				continue;
author maintainer
#691 lore raw

PATCH 685/713 wifi: mwifiex: Detach sync cmd buffer on interrupted wait

In reply to Greg Kroah-Hartman

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

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

From: Fabio Estevam <[email protected]>

commit ef06882c7d8a7400b67d0d003b1008093dd589ed upstream.

mwifiex synchronous commands keep the caller-provided data buffer in cmd_node->data_buf. Several callers pass stack-allocated objects there.

If wait_event_interruptible_timeout() is interrupted, the caller can return and release that stack object while the firmware command is still the current command. A late firmware response then reaches the normal response handler, which can copy data through cmd_node->data_buf into the stale stack address.

This fixes a stack corruption observed during repeated association and disassociation cycles. The panic trace showed the command wait being interrupted immediately before a bad pointer dereference:

cmd_wait_q terminated: -512

  Unable to handle kernel paging request at virtual address 002c583837384662
  Kernel panic - not syncing: stack-protector: Kernel stack is corrupted
  ...
  Tainted: [M]=MACHINE_CHECK

The fault address decodes as little-endian ASCII:

0x002c583837384662 -> "bF878X,\0"

which is a fragment of the VERSION_EXT firmware string exposed as debugfs "verext":

w8997o-V4, RF878X, FP92, 16.92.21.p153.7

The same runs also showed corrupted control data containing:

0x2400372e333531 -> "153.7\0$"

which is the tail of the same VERSION_EXT string. This points at a late VERSION_EXT response writing through a stale stack-backed data_buf after the interrupted wait returned.

After cancelling pending commands on an interrupted or timed-out wait, detach the caller-owned data buffer from the still-current command. This preserves the existing command cancellation behaviour while preventing a late response from writing through a pointer whose lifetime ended with the waiting caller.

Tested on an i.MX8MP board using an 88W8997.

---
 drivers/net/wireless/marvell/mwifiex/sta_ioctl.c |   12 ++++++++++++
 1 file changed, 12 insertions(+)
drivers/net/wireless/marvell/mwifiex/sta_ioctl.c +12 -0
@@ -57,6 +57,18 @@ int mwifiex_wait_queue_complete(struct m
 		mwifiex_dbg(adapter, ERROR, "cmd_wait_q terminated: %d\n",
 			    status);
 		mwifiex_cancel_all_pending_cmd(adapter);
+
+		/* The command response path writes through cmd_node->data_buf.
+		 * On an interrupted wait, the caller can return and release a
+		 * stack-allocated data_buf before a late firmware response is
+		 * processed. Detach the caller-owned buffer from the current
+		 * command so a late response cannot corrupt freed stack memory.
+		 */
+		spin_lock_bh(&adapter->mwifiex_cmd_lock);
+		if (adapter->curr_cmd == cmd_queued)
+			adapter->curr_cmd->data_buf = NULL;
+		spin_unlock_bh(&adapter->mwifiex_cmd_lock);
+
 		return status;
 	}
 
author maintainer
#692 lore raw

PATCH 686/713 wifi: rtl818x: initialize eeprom_93cx6 struct to zero

In reply to Greg Kroah-Hartman

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

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

From: Stanislaw Gruszka <[email protected]>

commit 799b5f45cb8194ebd06c9c89e0afdad5bedd2cc5 upstream.

Commit 7738a7ab9d12 ("misc: eeprom: eeprom_93cx6: Add quirk for extra read clock cycle") added extra 'quirk' field to struct eeprom_93cx6.

Many existing users of eeprom_93cx6, including rtl818x drivers, allocate the structure on the stack without initializing all fields. As a result, the added quirk field has an undefined value and can randomly cause reading wrong data from the EEPROM.

Fix by initializing the structures with {}.

Fixes 7738a7ab9d12 ("misc: eeprom: eeprom_93cx6: Add quirk for extra read clock cycle")
Cc [email protected] # v6.13+
---
 drivers/net/wireless/realtek/rtl818x/rtl8180/dev.c |    2 +-
 drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c |    2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)
drivers/net/wireless/realtek/rtl818x/rtl8180/dev.c +1 -1
@@ -1652,7 +1652,7 @@ static void rtl8180_eeprom_register_writ
 
 static void rtl8180_eeprom_read(struct rtl8180_priv *priv)
 {
-	struct eeprom_93cx6 eeprom;
+	struct eeprom_93cx6 eeprom = {};
 	int eeprom_cck_table_adr;
 	u16 eeprom_val;
 	int i;
drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c +1 -1
@@ -1445,7 +1445,7 @@ static int rtl8187_probe(struct usb_inte
 	struct usb_device *udev = interface_to_usbdev(intf);
 	struct ieee80211_hw *dev;
 	struct rtl8187_priv *priv;
-	struct eeprom_93cx6 eeprom;
+	struct eeprom_93cx6 eeprom = {};
 	struct ieee80211_channel *channel;
 	const char *chip_name;
 	u16 txpwr, reg;
author maintainer
#693 lore raw

PATCH 687/713 wifi: rtlwifi: rtl8192du: check QoS TID before indexing tids

In reply to Greg Kroah-Hartman

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

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

From: Runyu Xiao <[email protected]>

commit ed4f05d9f2f42fd866f55108db8123eefcc5fb33 upstream.

rtl92du_tx_fill_desc() uses ieee80211_get_tid() to read the QoS TID from the 802.11 header and then uses it as an index into sta_entry->tids[]. ieee80211_get_tid() returns the low 4-bit QoS TID value, so the result can be in the range 0..15.

rtlwifi only allocates MAX_TID_COUNT entries for sta_entry->tids[], and MAX_TID_COUNT is 9. A QoS TID greater than 8 therefore indexes past the aggregation state array. Keep the default RTL_AGG_STOP state for out-of-range TIDs, matching rtl92cu_tx_fill_desc().

This issue was detected by our static analysis tool and confirmed by manual audit. UBSAN validation for the same bug pattern reports an array-index-out-of-bounds access with index 10 for type 'rtl_tid_data [9]'.

---
 drivers/net/wireless/realtek/rtlwifi/rtl8192du/trx.c |    3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/net/wireless/realtek/rtlwifi/rtl8192du/trx.c +2 -1
@@ -106,7 +106,8 @@ void rtl92du_tx_fill_desc(struct ieee802
 	if (sta) {
 		sta_entry = (struct rtl_sta_info *)sta->drv_priv;
 		tid = ieee80211_get_tid(hdr);
-		agg_state = sta_entry->tids[tid].agg.agg_state;
+		if (tid < MAX_TID_COUNT)
+			agg_state = sta_entry->tids[tid].agg.agg_state;
 		ampdu_density = sta->deflink.ht_cap.ampdu_density;
 	}
 
author maintainer
#694 lore raw

PATCH 688/713 wifi: rtlwifi: rtl8192du: Fix possible memory leak in rtl92du_init_sw_vars()

In reply to Greg Kroah-Hartman

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

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

From: Abdun Nihaal <[email protected]>

commit 6496ce90845df2d22fb8e8ed235cd2936fad41c8 upstream.

The memory allocated inside rtl92du_init_shared_data() is not freed in any of the subsequent error paths in rtl92du_init_sw_vars(). Fix that by adding a call to rtl92du_deinit_shared_data() in the error path.

---
 drivers/net/wireless/realtek/rtlwifi/rtl8192du/sw.c |   12 +++++++++---
 1 file changed, 9 insertions(+), 3 deletions(-)
drivers/net/wireless/realtek/rtlwifi/rtl8192du/sw.c +9 -3
@@ -145,8 +145,10 @@ static int rtl92du_init_sw_vars(struct i
 
 	/* for firmware buf */
 	rtlpriv->rtlhal.pfirmware = kmalloc(0x8000, GFP_KERNEL);
-	if (!rtlpriv->rtlhal.pfirmware)
-		return -ENOMEM;
+	if (!rtlpriv->rtlhal.pfirmware) {
+		err = -ENOMEM;
+		goto error;
+	}
 
 	rtlpriv->max_fw_size = 0x8000;
 	pr_info("Driver for Realtek RTL8192DU WLAN interface\n");
@@ -160,10 +162,14 @@ static int rtl92du_init_sw_vars(struct i
 		pr_err("Failed to request firmware!\n");
 		kfree(rtlpriv->rtlhal.pfirmware);
 		rtlpriv->rtlhal.pfirmware = NULL;
-		return err;
+		goto error;
 	}
 
 	return 0;
+
+error:
+	rtl92du_deinit_shared_data(hw);
+	return err;
 }
 
 static void rtl92du_deinit_sw_vars(struct ieee80211_hw *hw)
author maintainer
#695 lore raw

PATCH 689/713 wifi: rtw88: Fix potential memory leak in rtw_txq_push_skb()

In reply to Greg Kroah-Hartman

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

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

From: Abdun Nihaal <[email protected]>

commit 9f2948010764d708bda27369d09ce6f194abe8e3 upstream.

The skb passed to the rtw_hci_tx_write() is expected to be freed when the function fails, but the error path in rtw_txq_push_skb() does not free the skb before returning. This can lead to a memory leak in rtw_txq_push() where a dequeued skb is passed to rtw_txq_push_skb().

---
 drivers/net/wireless/realtek/rtw88/tx.c |    1 +
 1 file changed, 1 insertion(+)
drivers/net/wireless/realtek/rtw88/tx.c +1 -0
@@ -619,6 +619,7 @@ static int rtw_txq_push_skb(struct rtw_d
 	ret = rtw_hci_tx_write(rtwdev, &pkt_info, skb);
 	if (ret) {
 		rtw_err(rtwdev, "failed to write TX skb to HCI\n");
+		ieee80211_free_txskb(rtwdev->hw, skb);
 		return ret;
 	}
 	return 0;
author maintainer
#696 lore raw

PATCH 690/713 wifi: rtw88: pci: fix resource leak on failed NAPI setup

In reply to Greg Kroah-Hartman

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

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

From: Dawei Feng <[email protected]>

commit e779df4806cd29cbcca5c9dc0a1073662c76b889 upstream.

rtw_pci_probe() allocates PCI resources through rtw_pci_setup_resource() before it sets up NAPI. If rtw_pci_napi_init() fails, the error path jumps straight to err_pci_declaim and skips rtw_pci_destroy(), leaving the PCI resources allocated by rtw_pci_setup_resource() behind.

Add a dedicated cleanup label for the NAPI setup failure path so probe destroys the PCI resources.

The bug was first flagged by an experimental analysis tool we are developing for kernel memory-management bugs while analyzing current mainline kernels. The tool is still under development and is not yet publicly available. Manual inspection confirms that the bug is still present in v7.1-rc7.

An x86_64 allyesconfig build showed no new warnings. As we do not have a suitable rtw88 PCI board to test with, no runtime testing was able to be performed.

---
 drivers/net/wireless/realtek/rtw88/pci.c |    4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/net/wireless/realtek/rtw88/pci.c +3 -1
@@ -1834,7 +1834,7 @@ int rtw_pci_probe(struct pci_dev *pdev,
 	ret = rtw_pci_napi_init(rtwdev);
 	if (ret) {
 		rtw_err(rtwdev, "failed to setup NAPI\n");
-		goto err_pci_declaim;
+		goto err_destroy_rsrc;
 	}
 
 	ret = rtw_chip_info_setup(rtwdev);
@@ -1866,6 +1866,8 @@ int rtw_pci_probe(struct pci_dev *pdev,
 
 err_destroy_pci:
 	rtw_pci_napi_deinit(rtwdev);
+
+err_destroy_rsrc:
 	rtw_pci_destroy(rtwdev, pdev);
 
 err_pci_declaim:
author maintainer
#697 lore raw

PATCH 691/713 wifi: rtw89: pci: add .shutdown callback to stop rfkill polling on reboot

In reply to Greg Kroah-Hartman

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

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

From: Yuhang.chen <[email protected]>

commit 667c12782aaf8dd3cb2213e528fe63a73cb63345 upstream.

Since the hardware rfkill polling was introduced, arm64 platforms can panic with an asynchronous SError during warm reboot:

SError Interrupt on CPU8, code 0x00000000be000011 -- SError

  Workqueue: events_power_efficient rfkill_poll [rfkill]
    rtw89_pci_ops_read8+0x94/0x160 [rtw89_pci]
    rtw89_core_rfkill_poll+0x50/0x1e0 [rtw89_core]
    rtw89_ops_rfkill_poll+0x40/0x68 [rtw89_core]
    ieee80211_rfkill_poll+0x3c/0x70 [mac80211]
    cfg80211_rfkill_poll+0x40/0x2a0 [cfg80211]
    rfkill_poll+0x30/0x88 [rfkill]
  Kernel panic - not syncing: Asynchronous SError Interrupt

On the reboot path the kernel only runs device_shutdown(), which calls each driver's .shutdown callback; .remove is not invoked. The rtw89 PCI driver had no .shutdown callback, so nothing stopped the rfkill polling work while the platform was tearing the PCIe link down. Once the link is gone, the next MMIO read from the poll handler targets a non-responding device and is reported as a fatal asynchronous SError on arm64.

Add rtw89_pci_shutdown(), wired to all rtw89 PCI device drivers, which sets a new RTW89_FLAG_SHUTDOWN flag (mirroring the USB RTW89_FLAG_UNPLUGGED pattern). When the flag is set, rtw89_ops_rfkill_poll() returns early, so no MMIO read is issued to the chip after shutdown begins and the SError no longer occurs.

This does not call the full .remove path from .shutdown, to keep the shutdown handler minimal and avoid running the non-idempotent teardown twice.

---
 drivers/net/wireless/realtek/rtw89/core.h       |    1 +
 drivers/net/wireless/realtek/rtw89/mac80211.c   |    3 ++-
 drivers/net/wireless/realtek/rtw89/pci.c        |   13 +++++++++++++
 drivers/net/wireless/realtek/rtw89/pci.h        |    1 +
 drivers/net/wireless/realtek/rtw89/rtw8851be.c  |    1 +
 drivers/net/wireless/realtek/rtw89/rtw8852ae.c  |    1 +
 drivers/net/wireless/realtek/rtw89/rtw8852be.c  |    1 +
 drivers/net/wireless/realtek/rtw89/rtw8852bte.c |    1 +
 drivers/net/wireless/realtek/rtw89/rtw8852ce.c  |    1 +
 drivers/net/wireless/realtek/rtw89/rtw8922ae.c  |    1 +
 drivers/net/wireless/realtek/rtw89/rtw8922de.c  |    1 +
 11 files changed, 24 insertions(+), 1 deletion(-)
drivers/net/wireless/realtek/rtw89/core.h +1 -0
@@ -5373,6 +5373,7 @@ enum rtw89_flags {
 	RTW89_FLAG_CHANGING_INTERFACE,
 	RTW89_FLAG_HW_RFKILL_STATE,
 	RTW89_FLAG_UNPLUGGED,
+	RTW89_FLAG_SHUTDOWN,
 
 	NUM_OF_RTW89_FLAGS,
 };
drivers/net/wireless/realtek/rtw89/mac80211.c +2 -1
@@ -1979,7 +1979,8 @@ static void rtw89_ops_rfkill_poll(struct
 	lockdep_assert_wiphy(hw->wiphy);
 
 	/* wl_disable GPIO get floating when entering LPS */
-	if (test_bit(RTW89_FLAG_RUNNING, rtwdev->flags))
+	if (test_bit(RTW89_FLAG_RUNNING, rtwdev->flags) ||
+	    test_bit(RTW89_FLAG_SHUTDOWN, rtwdev->flags))
 		return;
 
 	rtw89_core_rfkill_poll(rtwdev, false);
drivers/net/wireless/realtek/rtw89/pci.c +13 -0
@@ -4874,6 +4874,19 @@ void rtw89_pci_remove(struct pci_dev *pd
 }
 EXPORT_SYMBOL(rtw89_pci_remove);
 
+void rtw89_pci_shutdown(struct pci_dev *pdev)
+{
+	struct ieee80211_hw *hw = pci_get_drvdata(pdev);
+	struct rtw89_dev *rtwdev;
+
+	if (!hw)
+		return;
+
+	rtwdev = hw->priv;
+	set_bit(RTW89_FLAG_SHUTDOWN, rtwdev->flags);
+}
+EXPORT_SYMBOL(rtw89_pci_shutdown);
+
 MODULE_AUTHOR("Realtek Corporation");
 MODULE_DESCRIPTION("Realtek PCI 802.11ax wireless driver");
 MODULE_LICENSE("Dual BSD/GPL");
drivers/net/wireless/realtek/rtw89/pci.h +1 -0
@@ -1751,6 +1751,7 @@ struct pci_device_id;
 
 int rtw89_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id);
 void rtw89_pci_remove(struct pci_dev *pdev);
+void rtw89_pci_shutdown(struct pci_dev *pdev);
 void rtw89_pci_basic_cfg(struct rtw89_dev *rtwdev, bool resume);
 void rtw89_pci_ops_reset(struct rtw89_dev *rtwdev);
 int rtw89_pci_ltr_set(struct rtw89_dev *rtwdev, bool en);
drivers/net/wireless/realtek/rtw89/rtw8851be.c +1 -0
@@ -93,6 +93,7 @@ static struct pci_driver rtw89_8851be_dr
 	.id_table	= rtw89_8851be_id_table,
 	.probe		= rtw89_pci_probe,
 	.remove		= rtw89_pci_remove,
+	.shutdown	= rtw89_pci_shutdown,
 	.driver.pm	= &rtw89_pm_ops,
 	.err_handler    = &rtw89_pci_err_handler,
 };
drivers/net/wireless/realtek/rtw89/rtw8852ae.c +1 -0
@@ -95,6 +95,7 @@ static struct pci_driver rtw89_8852ae_dr
 	.id_table	= rtw89_8852ae_id_table,
 	.probe		= rtw89_pci_probe,
 	.remove		= rtw89_pci_remove,
+	.shutdown	= rtw89_pci_shutdown,
 	.driver.pm	= &rtw89_pm_ops,
 	.err_handler    = &rtw89_pci_err_handler,
 };
drivers/net/wireless/realtek/rtw89/rtw8852be.c +1 -0
@@ -97,6 +97,7 @@ static struct pci_driver rtw89_8852be_dr
 	.id_table	= rtw89_8852be_id_table,
 	.probe		= rtw89_pci_probe,
 	.remove		= rtw89_pci_remove,
+	.shutdown	= rtw89_pci_shutdown,
 	.driver.pm	= &rtw89_pm_ops,
 	.err_handler    = &rtw89_pci_err_handler,
 };
drivers/net/wireless/realtek/rtw89/rtw8852bte.c +1 -0
@@ -99,6 +99,7 @@ static struct pci_driver rtw89_8852bte_d
 	.id_table	= rtw89_8852bte_id_table,
 	.probe		= rtw89_pci_probe,
 	.remove		= rtw89_pci_remove,
+	.shutdown	= rtw89_pci_shutdown,
 	.driver.pm	= &rtw89_pm_ops,
 	.err_handler    = &rtw89_pci_err_handler,
 };
drivers/net/wireless/realtek/rtw89/rtw8852ce.c +1 -0
@@ -122,6 +122,7 @@ static struct pci_driver rtw89_8852ce_dr
 	.id_table	= rtw89_8852ce_id_table,
 	.probe		= rtw89_pci_probe,
 	.remove		= rtw89_pci_remove,
+	.shutdown	= rtw89_pci_shutdown,
 	.driver.pm	= &rtw89_pm_ops,
 	.err_handler    = &rtw89_pci_err_handler,
 };
drivers/net/wireless/realtek/rtw89/rtw8922ae.c +1 -0
@@ -111,6 +111,7 @@ static struct pci_driver rtw89_8922ae_dr
 	.id_table	= rtw89_8922ae_id_table,
 	.probe		= rtw89_pci_probe,
 	.remove		= rtw89_pci_remove,
+	.shutdown	= rtw89_pci_shutdown,
 	.driver.pm	= &rtw89_pm_ops_be,
 	.err_handler    = &rtw89_pci_err_handler,
 };
drivers/net/wireless/realtek/rtw89/rtw8922de.c +1 -0
@@ -111,6 +111,7 @@ static struct pci_driver rtw89_8922de_dr
 	.id_table	= rtw89_8922de_id_table,
 	.probe		= rtw89_pci_probe,
 	.remove		= rtw89_pci_remove,
+	.shutdown	= rtw89_pci_shutdown,
 	.driver.pm	= &rtw89_pm_ops_be,
 	.err_handler    = &rtw89_pci_err_handler,
 };
author maintainer
#698 lore raw

PATCH 692/713 wifi: mt76: mt7615: avoid waiting for mac work under the mt76 mutex

In reply to Greg Kroah-Hartman

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

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

From: Runyu Xiao <[email protected]>

commit bda8324270b1ac91bfba1df8928e0570e29759e8 upstream.

mt7615_suspend() acquired the mt76 mutex and then called cancel_delayed_work_sync() on mac_work. mt7615_mac_work() acquires the same mutex via mt7615_mutex_acquire() at the top of the worker, so if mac_work is already running and blocked on the mutex, the suspend path deadlocks waiting for the work it holds the mutex against.

Flush scan_work and mac_work before taking the mutex, matching the suspend paths in mt7921 and mt7925. scan_work only takes the mt76 spinlock, but moving it keeps the sequence consistent. This also keeps mac_work from running over an already suspended HIF, which the previous split (async cancel under the lock, sync cancel after release) would have allowed.

---
 drivers/net/wireless/mediatek/mt76/mt7615/main.c |    5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)
drivers/net/wireless/mediatek/mt76/mt7615/main.c +3 -2
@@ -1239,11 +1239,12 @@ static int mt7615_suspend(struct ieee802
 	cancel_delayed_work_sync(&dev->pm.ps_work);
 	mt76_connac_free_pending_tx_skbs(&dev->pm, NULL);
 
+	cancel_delayed_work_sync(&phy->scan_work);
+	cancel_delayed_work_sync(&phy->mt76->mac_work);
+
 	mt7615_mutex_acquire(dev);
 
 	clear_bit(MT76_STATE_RUNNING, &phy->mt76->state);
-	cancel_delayed_work_sync(&phy->scan_work);
-	cancel_delayed_work_sync(&phy->mt76->mac_work);
 
 	set_bit(MT76_STATE_SUSPEND, &phy->mt76->state);
 	ieee80211_iterate_active_interfaces(hw,
author maintainer
#699 lore raw

PATCH 693/713 wifi: mt76: mt7915: bound the device EEPROM address before the EFUSE copy

In reply to Greg Kroah-Hartman

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

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

From: Bryam Vargas <[email protected]>

commit 44b5adfe49499f53002737f5fe81d608c08122fc upstream.

mt7915_mcu_get_eeprom() copies a fixed EFUSE block into the driver's dev->mt76.eeprom.data buffer at the offset reported by the MCU response (res->addr, a device-controlled __le32) without checking it against the buffer size. A malicious or malfunctioning device can report an arbitrary address and drive a 16-byte out-of-bounds write past eeprom.data.

Reject a response whose address would place the copy outside eeprom.data before deriving the destination pointer. Devices that echo the requested in-bounds offset are unaffected.

Fixes e57b7901469f ("mt76: add mac80211 driver for MT7915 PCIe-based chipsets")
---
 drivers/net/wireless/mediatek/mt76/mt7915/mcu.c |   11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)
drivers/net/wireless/mediatek/mt76/mt7915/mcu.c +9 -2
@@ -2917,8 +2917,15 @@ int mt7915_mcu_get_eeprom(struct mt7915_
 		return ret;
 
 	res = (struct mt7915_mcu_eeprom_info *)skb->data;
-	if (!buf)
-		buf = dev->mt76.eeprom.data + le32_to_cpu(res->addr);
+	if (!buf) {
+		u32 addr = le32_to_cpu(res->addr);
+
+		if (addr > dev->mt76.eeprom.size - MT7915_EEPROM_BLOCK_SIZE) {
+			dev_kfree_skb(skb);
+			return -EINVAL;
+		}
+		buf = dev->mt76.eeprom.data + addr;
+	}
 	memcpy(buf, res->data, MT7915_EEPROM_BLOCK_SIZE);
 
 	dev_kfree_skb(skb);
author maintainer
#700 lore raw

PATCH 694/713 wifi: mt76: mt7925: cancel mlo_pm_work on stop

In reply to Greg Kroah-Hartman

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

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

From: Devin Wittmayer <[email protected]>

commit 81faf578320df2dfc682a96baa6e85851dd68b6f upstream.

mt7925 queues mlo_pm_work with a 5 second delay during multi-link power-save setup and never cancels it on the stop path. If the device is torn down inside that window, the work outlives the teardown and its timer fires afterwards, trying to queue onto the workqueue that is already gone:

workqueue: cannot queue mt7925_mlo_pm_work [mt7925_common] on wq phy0

  WARNING: kernel/workqueue.c:2283 at __queue_work+0x59/0xa0, CPU#1: swapper/1/0
   call_timer_fn+0x2a/0x140
   __run_timers+0x203/0x330
   run_timer_softirq+0x86/0xf0

mt7921 already has its own stop callback, so add one for mt7925 that cancels the work before calling mt792x_stop(). mt7925_ops backs both the PCIe and USB drivers, so this covers both.

---
 drivers/net/wireless/mediatek/mt76/mt7925/main.c |   11 ++++++++++-
 1 file changed, 10 insertions(+), 1 deletion(-)
drivers/net/wireless/mediatek/mt76/mt7925/main.c +10 -1
@@ -2485,10 +2485,19 @@ static void mt7925_channel_switch_rx_bea
 	}
 }
 
+static void mt7925_stop(struct ieee80211_hw *hw, bool suspend)
+{
+	struct mt792x_dev *dev = mt792x_hw_dev(hw);
+
+	cancel_delayed_work_sync(&dev->mlo_pm_work);
+
+	mt792x_stop(hw, suspend);
+}
+
 const struct ieee80211_ops mt7925_ops = {
 	.tx = mt792x_tx,
 	.start = mt7925_start,
-	.stop = mt792x_stop,
+	.stop = mt7925_stop,
 	.add_interface = mt7925_add_interface,
 	.remove_interface = mt792x_remove_interface,
 	.config = mt7925_config,
author maintainer
#701 lore raw

PATCH 695/713 wifi: mt76: mt7996: bound the device EEPROM address before the EFUSE copy

In reply to Greg Kroah-Hartman

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

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

From: Bryam Vargas <[email protected]>

commit 13b3c29a782033ce4a230be9e5618032813dbcd4 upstream.

mt7996_mcu_get_eeprom() derives the destination of the EFUSE/EXT block copy from the address reported by the MCU response (event->addr, a device-controlled __le32) and clamps only the copy length, never the destination offset into dev->mt76.eeprom.data. A malicious or malfunctioning device can report an arbitrary address and drive an out-of-bounds write of up to MT7996_EXT_EEPROM_BLOCK_SIZE bytes past eeprom.data.

Reject a response whose address would place the copy outside eeprom.data before deriving the destination pointer. Devices that echo the requested in-bounds offset are unaffected.

Fixes 98686cd21624 ("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, 8 insertions(+), 1 deletion(-)
drivers/net/wireless/mediatek/mt76/mt7996/mcu.c +8 -1
@@ -4352,11 +4352,18 @@ int mt7996_mcu_get_eeprom(struct mt7996_
 	event = (struct mt7996_mcu_eeprom_access_event *)skb->data;
 	if (event->valid) {
 		u32 ret_len = le32_to_cpu(event->eeprom.ext_eeprom.data_len);
+		u32 block = mode == EEPROM_MODE_EXT ? MT7996_EXT_EEPROM_BLOCK_SIZE :
+						      MT7996_EEPROM_BLOCK_SIZE;
 
 		addr = le32_to_cpu(event->addr);
 
-		if (!buf)
+		if (!buf) {
+			if (addr > dev->mt76.eeprom.size - block) {
+				dev_kfree_skb(skb);
+				return -EINVAL;
+			}
 			buf = (u8 *)dev->mt76.eeprom.data + addr;
+		}
 
 		switch (mode) {
 		case EEPROM_MODE_EFUSE:
author maintainer
#702 lore raw

PATCH 696/713 wifi: mt76: mt7996: fix TX DMA mapping leak for AddBA req frames

In reply to Greg Kroah-Hartman

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

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

From: Felix Fietkau <[email protected]>

commit deaa2e3656937fbbe312f0ee2616c756c6e2511f upstream.

mt7996/mt7992 hand the firmware a HW MAC-TXP for AddBA req action frames (MT_TXD7_MAC_TXD, set in mt7996_mac_write_txwi_80211()), but are otherwise FW-TXP devices. On tx free mt76_connac_txp_skb_unmap() therefore decodes the per-frame txp as a struct mt76_connac_fw_txp. For a MAC-TXP the fw_txp.nbuf byte aliases the AddBA TID word (MT_TXP1_TID_ADDBA), which is always zero, so the unmap loop runs zero times and the skb DMA mapping in buf[1] is never unmapped. buf[1].skip_unmap is set unconditionally, so the generic DMA-ring cleanup skips it as well.

Each AddBA req therefore leaks one TX DMA mapping, roughly one per (re)association. With WED enabled these mappings are bounced through the WED swiotlb pool, so under continuous client reconnect churn the pool is exhausted after ~1-2 days, after which DMA mapping fails for WED, the WiFi MCU and other on-SoC consumers.

Keep the deferred (token release) unmap that the design relies on, and add an mt7996-specific txp unmap that inspects MT_TXD7_MAC_TXD and unmaps buf[1] from the MAC-TXP layout for those frames, delegating to mt76_connac_txp_skb_unmap() otherwise.

Fixes cb6ebbdffef2 ("wifi: mt76: mt7996: support writing MAC TXD for AddBA Request")
---
 drivers/net/wireless/mediatek/mt76/mt7996/mac.c |   26 +++++++++++++++++++++++-
 1 file changed, 25 insertions(+), 1 deletion(-)
drivers/net/wireless/mediatek/mt76/mt7996/mac.c +25 -1
@@ -1240,6 +1240,30 @@ mt7996_tx_check_aggr(struct ieee80211_li
 }
 
 static void
+mt7996_txp_skb_unmap(struct mt76_dev *mdev, struct mt76_txwi_cache *t)
+{
+	u8 *txwi_ptr = mt76_get_txwi_ptr(mdev, t);
+	__le32 *txwi = (__le32 *)txwi_ptr;
+	__le32 *txp;
+	dma_addr_t addr;
+	u32 val;
+
+	if (!(le32_to_cpu(txwi[7]) & MT_TXD7_MAC_TXD)) {
+		mt76_connac_txp_skb_unmap(mdev, t);
+		return;
+	}
+
+	txp = (__le32 *)(txwi_ptr + MT_TXD_SIZE);
+	val = le32_to_cpu(txp[3]);
+	addr = le32_to_cpu(txp[2]);
+#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
+	addr |= (dma_addr_t)FIELD_GET(MT_TXP3_DMA_ADDR_H, val) << 32;
+#endif
+	dma_unmap_single(mdev->dma_dev, addr, FIELD_GET(MT_TXP_BUF_LEN, val),
+			 DMA_TO_DEVICE);
+}
+
+static void
 mt7996_txwi_free(struct mt7996_dev *dev, struct mt76_txwi_cache *t,
 		 struct ieee80211_link_sta *link_sta,
 		 struct mt76_wcid *wcid, struct list_head *free_list)
@@ -1248,7 +1272,7 @@ mt7996_txwi_free(struct mt7996_dev *dev,
 	__le32 *txwi;
 	u16 wcid_idx;
 
-	mt76_connac_txp_skb_unmap(mdev, t);
+	mt7996_txp_skb_unmap(mdev, t);
 	if (!t->skb)
 		goto out;
 
author maintainer
#703 lore raw

PATCH 697/713 wifi: mt76: mt7996: validate default EEPROM firmware size

In reply to Greg Kroah-Hartman

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

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

From: Laxman Acharya Padhya <[email protected]>

commit 653c6e289b13cc6942f3e8f8e3c568e70fa42d1f upstream.

The default EEPROM firmware is parsed and copied as a full EEPROM without checking its length. A truncated file can make the driver read beyond the firmware buffer during variant validation or the fallback copy.

Reject files shorter than MT7996_EEPROM_SIZE before parsing or copying the firmware.

Fixes 98686cd21624 ("wifi: mt76: mt7996: add driver for MediaTek Wi-Fi 7 (802.11be) devices")
---
 drivers/net/wireless/mediatek/mt76/mt7996/eeprom.c |    6 ++++++
 1 file changed, 6 insertions(+)
drivers/net/wireless/mediatek/mt76/mt7996/eeprom.c +6 -0
@@ -150,6 +150,12 @@ mt7996_eeprom_check_or_use_default(struc
 		goto out;
 	}
 
+	if (fw->size < MT7996_EEPROM_SIZE) {
+		dev_err(dev->mt76.dev, "Invalid default bin size\n");
+		ret = -EINVAL;
+		goto out;
+	}
+
 	if (!use_default && mt7996_eeprom_variant_valid(dev, fw->data))
 		goto out;
 
author maintainer
#704 lore raw

PATCH 698/713 vsock/virtio: flush works in dependency order

In reply to Greg Kroah-Hartman

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

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

From: Chengfeng Ye <[email protected]>

commit 728836ebca239810f164262b10211ef59182f811 upstream.

virtio_vsock_remove() stops the virtqueues and then flushes each work item before freeing the enclosing virtio_vsock. The current order does not account for dependencies between those items: tx_work may queue send_pkt_work, and send_pkt_work may queue rx_work.

In particular, send_pkt_work can set restart_rx and release tx_lock. The remove path can then stop the queues and flush rx_work before send_pkt_work queues it. Although the later send_pkt_work flush waits for that producer to finish, nothing waits for the newly queued rx_work, so kfree(vsock) can race with it.

KASAN reported:

  BUG: KASAN: slab-use-after-free in
  virtio_transport_rx_work+0x487/0x4b0
  Read of size 8 at addr ffff888114c2b008 by task kworker/1:1/47
  Workqueue: virtio_vsock virtio_transport_rx_work
  Call Trace:
   virtio_transport_rx_work+0x487/0x4b0
   process_one_work+0x688/0x1120
   worker_thread+0x45b/0xd10
  Allocated by task 1:
   virtio_vsock_probe+0xef/0x6b0
  Freed by task 84:
   kfree+0x131/0x3c0
   virtio_vsock_remove+0xd1/0x100

Flush the works in producer-to-consumer order. virtio_vsock_vqs_del() has already disabled the queue callbacks and cleared the run flags, so after tx_work and send_pkt_work are drained, no source remains that can queue rx_work after its flush.

---
 net/vmw_vsock/virtio_transport.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
net/vmw_vsock/virtio_transport.c +1 -1
@@ -872,10 +872,10 @@ static void virtio_vsock_remove(struct v
 	/* Other works can be queued before 'config->del_vqs()', so we flush
 	 * all works before to free the vsock object to avoid use after free.
 	 */
-	flush_work(&vsock->rx_work);
 	flush_work(&vsock->tx_work);
 	flush_work(&vsock->event_work);
 	flush_work(&vsock->send_pkt_work);
+	flush_work(&vsock->rx_work);
 
 	mutex_unlock(&the_virtio_vsock_mutex);
 
author maintainer
#705 lore raw

PATCH 699/713 w1: ds28e17: reject an oversize length on an I2C block read

In reply to Greg Kroah-Hartman

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

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

From: Maoyi Xie <[email protected]>

commit 169ae5e65e5aaf213b6a578f6478a9fd2e523606 upstream.

w1_f19_i2c_master_transfer() is the master_xfer for the DS28E17 1-Wire to I2C bridge. On an I2C_M_RECV_LEN read, it takes the length from the device. The downstream slave puts a length byte in buf[0]. The driver then reads that many bytes into buf[1] with w1_f19_i2c_read().

buf[0] is controlled by the device and can be 0 to 255. w1_f19_i2c_read() only rejects a zero count. The caller buffer is I2C_SMBUS_BLOCK_MAX + 2, so 34 bytes. A length above 32 makes the read run past it, up to about 222 bytes out of bounds.

The SMBus core does check buf[0] against I2C_SMBUS_BLOCK_MAX. That check runs after master_xfer returns. By then the write is already done. i2c-algo-bit rejects an oversize length before it copies, and returns -EPROTO.

Reject a length above I2C_SMBUS_BLOCK_MAX at both RECV_LEN sites, the same way i2c-algo-bit does.

Fixes ebc4768ac497 ("add w1_ds28e17 driver for the DS28E17 Onewire to I2C master bridge")
---
 drivers/w1/slaves/w1_ds28e17.c |    8 ++++++++
 1 file changed, 8 insertions(+)
drivers/w1/slaves/w1_ds28e17.c +8 -0
@@ -389,6 +389,10 @@ static int w1_f19_i2c_master_transfer(st
 			 * another simple read in that case.
 			 */
 			if (msgs[i+1].flags & I2C_M_RECV_LEN) {
+				if (msgs[i+1].buf[0] > I2C_SMBUS_BLOCK_MAX) {
+					i = -EPROTO;
+					goto error;
+				}
 				result = w1_f19_i2c_read(sl, msgs[i+1].addr,
 					&(msgs[i+1].buf[1]), msgs[i+1].buf[0]);
 				if (result < 0) {
@@ -415,6 +419,10 @@ static int w1_f19_i2c_master_transfer(st
 			 * another simple read in that case.
 			 */
 			if (msgs[i].flags & I2C_M_RECV_LEN) {
+				if (msgs[i].buf[0] > I2C_SMBUS_BLOCK_MAX) {
+					i = -EPROTO;
+					goto error;
+				}
 				result = w1_f19_i2c_read(sl,
 					msgs[i].addr,
 					&(msgs[i].buf[1]),
author maintainer
#706 lore raw

PATCH 700/713 xarray: honor XA_FLAGS_ACCOUNT in xas_split_alloc()

In reply to Greg Kroah-Hartman

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

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

From: Zi Yan <[email protected]>

commit 789763523fb43cdc328de5cb5dcd19240ccf90d8 upstream.

XArray operations that allocate xa_nodes, such as xas_nomem() and xas_alloc(), add __GFP_ACCOUNT when the array has XA_FLAGS_ACCOUNT set. This charges the allocated memory and avoids the workingset convergence issue described by commit 7b785645e8f13 ("mm: fix page cache convergence regression").

xas_split_alloc() does not add _GFP_ACCOUNT when XA_FLAGS_ACCOUNT is present. Fix it.

Fixes 6b24ca4a1a8d ("mm: Use multi-index entries in the page cache")
Cc Baolin Wang <[email protected]>
Cc Barry Song <[email protected]>
Cc David Hildenbrand <[email protected]>
Cc Dev Jain <[email protected]>
Cc Lance Yang <[email protected]>
Cc Liam R. Howlett <[email protected]>
Cc Matthew Wilcox (Oracle) <[email protected]>
Cc Ryan Roberts <[email protected]>
Cc William Kucharski <[email protected]>
---
 lib/xarray.c |    3 +++
 1 file changed, 3 insertions(+)
lib/xarray.c +3 -0
@@ -1053,6 +1053,9 @@ void xas_split_alloc(struct xa_state *xa
 	if (xas->xa_shift + XA_CHUNK_SHIFT > order)
 		return;
 
+	if (xas->xa->xa_flags & XA_FLAGS_ACCOUNT)
+		gfp |= __GFP_ACCOUNT;
+
 	do {
 		struct xa_node *node;
 
author maintainer
#707 lore raw

PATCH 701/713 zloop: truncate finished zones to zone capacity

In reply to Greg Kroah-Hartman

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

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

From: Xu Rao <[email protected]>

commit 72e67c118642634c25465db0c8bcfa54c4ce086c upstream.

The size of a sequential zone backing file records the amount of data written and is used to restore the zone state. A backing file whose size is equal to the zone capacity is restored as a full zone, while a file larger than the zone capacity is rejected as invalid.

However, zloop_finish_zone() currently truncates the backing file to the zone size. For devices with a reduced zone capacity, finishing a zone therefore creates a backing file larger than the zone capacity. After the device is removed and later re-added, that zone file is rejected instead of being restored as a full zone.

Truncate finished sequential zones to the zone capacity, matching the persistent representation accepted by zloop_update_seq_zone() for a full zone.

---
 Documentation/admin-guide/blockdev/zoned_loop.rst |    9 ++++-----
 drivers/block/zloop.c                             |    3 ++-
 2 files changed, 6 insertions(+), 6 deletions(-)
Documentation/admin-guide/blockdev/zoned_loop.rst +4 -5
@@ -30,11 +30,10 @@ indicates the position of the write poin
 
 When resetting a sequential zone, its backing file size is truncated to zero.
 Conversely, for a zone finish operation, the backing file is truncated to the
-zone size. With this, the maximum capacity of a zloop zoned block device created
-can be larger configured to be larger than the storage space available on the
-backing file system. Of course, for such configuration, writing more data than
-the storage space available on the backing file system will result in write
-errors.
+zone capacity. With this, a zloop zoned block device can be configured with a
+larger capacity than the storage space available on the backing file system. Of
+course, for such configuration, writing more data than the storage space
+available on the backing file system will result in write errors.
 
 The zoned loop block device driver implements a complete zone transition state
 machine. That is, zones can be empty, implicitly opened, explicitly opened,
drivers/block/zloop.c +2 -1
@@ -478,7 +478,8 @@ static int zloop_finish_zone(struct zloo
 	    zone->cond == BLK_ZONE_COND_FULL)
 		goto unlock;
 
-	if (vfs_truncate(&zone->file->f_path, zlo->zone_size << SECTOR_SHIFT)) {
+	if (vfs_truncate(&zone->file->f_path,
+			 zlo->zone_capacity << SECTOR_SHIFT)) {
 		set_bit(ZLOOP_ZONE_SEQ_ERROR, &zone->flags);
 		ret = -EIO;
 		goto unlock;
author maintainer
#708 lore raw

PATCH 702/713 tpm: tpm_i2c_nuvoton: disable IRQ on wait timeout

In reply to Greg Kroah-Hartman

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

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

From: Myeonghun Pak <[email protected]>

commit 705c4ed0643366963547b2616d53165f2519c81f upstream.

i2c_nuvoton_wait_for_stat() enables the IRQ before waiting for the interrupt handler to report a status change. If the wait times out, or is interrupted before the handler runs, the function returns without balancing the enable_irq() call.

Disable the IRQ before leaving the failed wait path. Also preserve an interrupted wait's original error code instead of converting it to -ETIMEDOUT inside the helper.

---
 drivers/char/tpm/tpm_i2c_nuvoton.c |    6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)
drivers/char/tpm/tpm_i2c_nuvoton.c +4 -2
@@ -182,8 +182,10 @@ static int i2c_nuvoton_wait_for_stat(str
 						      timeout);
 		if (rc > 0)
 			return 0;
-		/* At this point we know that the SINT pin is asserted, so we
-		 * do not need to do i2c_nuvoton_check_status */
+
+		disable_irq(priv->irq);
+		if (rc < 0)
+			return rc;
 	} else {
 		unsigned long ten_msec, stop;
 		bool status_valid;
author maintainer
#709 lore raw

PATCH 703/713 sysctl: move the "cad_pid" entry from pid_table[] to kern_reboot_table[]

In reply to Greg Kroah-Hartman

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

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

From: Oleg Nesterov <[email protected]>

commit 7170ca01623b399c97f2ae9d3e228badc1f25ea3 upstream.

cad_pid is global, and kill_cad_pid() is only used in the root namespace.

However, due to pid_table_root_permissions(), a non-root user can unshare pid/user namespaces and modify it from the child namespace. This makes no sense and is simply wrong.

Move it to kern_reboot_table[] where it logically belongs; this ensures that only GLOBAL_ROOT_UID can read/modify this sysctl.

Note that this patch doesn't preserve "#ifdef CONFIG_PROC_SYSCTL" around the "cad_pid"; CONFIG_PROC_SYSCTL selects CONFIG_SYSCTL, so it is always set when kern_reboot_table[] is compiled.

---
 kernel/pid.c    |   31 -------------------------------
 kernel/reboot.c |   29 +++++++++++++++++++++++++++++
 2 files changed, 29 insertions(+), 31 deletions(-)
kernel/pid.c +0 -31
@@ -766,29 +766,6 @@ static struct ctl_table_root pid_table_r
 	.set_ownership	= pid_table_root_set_ownership,
 };
 
-static int proc_do_cad_pid(const struct ctl_table *table, int write, void *buffer,
-		size_t *lenp, loff_t *ppos)
-{
-	struct pid *new_pid;
-	pid_t tmp_pid;
-	int r;
-	struct ctl_table tmp_table = *table;
-
-	tmp_pid = pid_vnr(cad_pid);
-	tmp_table.data = &tmp_pid;
-
-	r = proc_dointvec(&tmp_table, write, buffer, lenp, ppos);
-	if (r || !write)
-		return r;
-
-	new_pid = find_get_pid(tmp_pid);
-	if (!new_pid)
-		return -ESRCH;
-
-	put_pid(xchg(&cad_pid, new_pid));
-	return 0;
-}
-
 static const struct ctl_table pid_table[] = {
 	{
 		.procname	= "pid_max",
@@ -799,14 +776,6 @@ static const struct ctl_table pid_table[
 		.extra1		= &pid_max_min,
 		.extra2		= &pid_max_max,
 	},
-#ifdef CONFIG_PROC_SYSCTL
-	{
-		.procname	= "cad_pid",
-		.maxlen		= sizeof(int),
-		.mode		= 0600,
-		.proc_handler	= proc_do_cad_pid,
-	},
-#endif
 };
 #endif
 
kernel/reboot.c +29 -0
@@ -1366,6 +1366,29 @@ static struct attribute *reboot_attrs[]
 };
 
 #ifdef CONFIG_SYSCTL
+static int proc_do_cad_pid(const struct ctl_table *table, int write, void *buffer,
+			   size_t *lenp, loff_t *ppos)
+{
+	struct ctl_table tmp_table = *table;
+	struct pid *new_pid;
+	pid_t tmp_pid;
+	int r;
+
+	tmp_pid = pid_vnr(cad_pid);
+	tmp_table.data = &tmp_pid;
+
+	r = proc_dointvec(&tmp_table, write, buffer, lenp, ppos);
+	if (r || !write)
+		return r;
+
+	new_pid = find_get_pid(tmp_pid);
+	if (!new_pid)
+		return -ESRCH;
+
+	put_pid(xchg(&cad_pid, new_pid));
+	return 0;
+}
+
 static const struct ctl_table kern_reboot_table[] = {
 	{
 		.procname       = "poweroff_cmd",
@@ -1381,6 +1404,12 @@ static const struct ctl_table kern_reboo
 		.mode           = 0644,
 		.proc_handler   = proc_dointvec,
 	},
+	{
+		.procname	= "cad_pid",
+		.maxlen		= sizeof(int),
+		.mode		= 0600,
+		.proc_handler	= proc_do_cad_pid,
+	},
 };
 
 static void __init kernel_reboot_sysctls_init(void)
author maintainer
#710 lore raw

PATCH 704/713 sticon/parisc: Detect default STI graphics card for console output

In reply to Greg Kroah-Hartman

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

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

From: Helge Deller <[email protected]>

commit de508ece1d37cdbbbfa52f074954310f9b066b13 upstream.

If a machine has multiple graphic cards, detect the graphic card which is used to display firmware messages and use that one as the default graphic card for sticon and fbcon.

On parisc machines the default graphic card used for BCH (boot console handler, aka BIOS menu) is stored in the stable storage (equivalent to CMOS storage on x86) or in the console path in page zero. Extract that path and store it as default STI path for later comparism. Take care that the graphic card can be a GSC or a PCI card which use different path strings.

Increase max string size for default_sti_path to 32 chars as the print_pa_hwpath() function formats a hardware path using unbounded sprintf calls for up to 6 bus converter components and 1 module component (e.g., 255/255/...), which can produce a string up to 28 bytes long.

---
 drivers/video/sticore.c |   22 +++++++++++++++++++++-
 1 file changed, 21 insertions(+), 1 deletion(-)
drivers/video/sticore.c +21 -1
@@ -325,7 +325,7 @@ static void sti_rom_copy(unsigned long b
 
 
 
-static char default_sti_path[21] __read_mostly;
+static char default_sti_path[32] __read_mostly;
 
 #ifndef MODULE
 static int __init sti_setup(char *str)
@@ -1148,6 +1148,26 @@ static void sti_init_roms(void)
 	pr_info("STI GSC/PCI core graphics driver "
 			STI_DRIVERVERSION "\n");
 
+	/*
+	 * Find default console by hardware path which is either stored in
+	 * console entry in stable storage or alternatively from console path
+	 * in PAGE0 used by BCH and PDC.
+	 */
+	if (!default_sti_path[0]) {
+		struct pdc_module_path conspath;
+		struct device *dev = NULL;
+
+		if (pdc_stable_read(0x60, &conspath, sizeof(conspath)) == PDC_OK)
+			dev = hwpath_to_device(&conspath.path);
+		if (!dev)
+			dev = hwpath_to_device(&PAGE0->mem_cons.dp.path);
+		if (dev && dev_is_pci(dev))
+			print_pci_hwpath(to_pci_dev(dev), default_sti_path);
+		else if (dev && !dev_is_pci(dev))
+			print_pa_hwpath(to_parisc_device(dev), default_sti_path);
+		pr_debug("default graphic card: %s\n", default_sti_path);
+	}
+
 	/* Register drivers for native & PCI cards */
 	register_parisc_driver(&pa_sti_driver);
 	WARN_ON(pci_register_driver(&pci_sti_driver));
author maintainer
#711 lore raw

PATCH 705/713 signal: avoid shared siginfo namespace rewrites

In reply to Greg Kroah-Hartman

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

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

From: Bradley Morgan <[email protected]>

commit d19cdc167e696714509e87d3f7ae765b6e164589 upstream.

send_signal_locked() rewrites sender ids for the target namespace. Group sends reuse the same siginfo, so one recipient can affect the next.

Copy the siginfo before changing it.

Fixes 7a0cf094944e ("signal: Correct namespace fixups of si_pid and si_uid")
Cc "Eric W. Biederman" <[email protected]>
Cc Adrian Huang <[email protected]>
Cc Aleksandr Nogikh <[email protected]>
Cc Christian Brauner <[email protected]>
Cc Marco Elver <[email protected]>
Cc "Masami Hiramatsu (Google)" <[email protected]>
Cc Mathieu Desnoyers <[email protected]>
Cc Peter Zijlstra <[email protected]>
Cc Steven Rostedt <[email protected]>
---
 kernel/signal.c |    4 ++++
 1 file changed, 4 insertions(+)
kernel/signal.c +4 -0
@@ -1181,6 +1181,7 @@ static inline bool has_si_pid_and_uid(st
 int send_signal_locked(int sig, struct kernel_siginfo *info,
 		       struct task_struct *t, enum pid_type type)
 {
+	struct kernel_siginfo rewritten;
 	/* Should SIGKILL or SIGSTOP be received by a pid namespace init? */
 	bool force = false;
 
@@ -1194,6 +1195,9 @@ int send_signal_locked(int sig, struct k
 		/* SIGKILL and SIGSTOP is special or has ids */
 		struct user_namespace *t_user_ns;
 
+		rewritten = *info;
+		info = &rewritten;
+
 		rcu_read_lock();
 		t_user_ns = task_cred_xxx(t, user_ns);
 		if (current_user_ns() != t_user_ns) {
author maintainer
#712 lore raw

PATCH 706/713 smack: fix cred UAF in smack_file_send_sigiotask()

In reply to Greg Kroah-Hartman

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

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

From: Jann Horn <[email protected]>

commit fedc88e38ce979a720cd2de042578cb5df3dc8de upstream.

When inspecting the credentials of another task, objective credentials (->real_cred, accessed with __task_cred()) must always be used.

Accessing ->cred on a non-current task is forbidden unless that task is being created or destroyed; a task is allowed to change its own ->cred pointer with no synchronization, and changing ->cred should only affect the current syscall.

smack_file_send_sigiotask() was accessing both sets of credentials: First tsk->cred, then __task_cred(tsk).

Fix it, always access the objective credentials here.

I have tested that this bug can lead to a KASAN-reported UAF of struct cred in smack_file_send_sigiotask(), and that this fix prevents the race.

---
 security/smack/smack_lsm.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
security/smack/smack_lsm.c +1 -1
@@ -1964,7 +1964,7 @@ static int smack_file_send_sigiotask(str
 {
 	struct smack_known **blob;
 	struct smack_known *skp;
-	struct smack_known *tkp = smk_of_task(smack_cred(tsk->cred));
+	struct smack_known *tkp = smk_of_task_struct_obj(tsk);
 	const struct cred *tcred;
 	struct file *file;
 	int rc;
author maintainer
#713 lore raw

PATCH 707/713 taskstats: fix cpumask parsing cutting off the last character

In reply to Greg Kroah-Hartman

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

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

From: Bradley Morgan <[email protected]>

commit 1f58a5335cdd14b3fb5f2a5d3763dee1f5cba1d3 upstream.

parse() hands nla_strscpy() len as dstsize, and nla_strscpy() copies at most dstsize - 1 bytes. When the attr payload comes in without a trailing NUL, srclen == len >= dstsize and the last character of the cpumask string gets cut off. Register "0-15" and you are silently listening on "0-1", exit data for the rest never shows up.

The bug only bites when the sender doesn't NUL terminate the payload; senders that include the NUL were always fine (srclen gets decremented for the trailing NUL, so srclen < dstsize). Thats probably why this survived 20 years. And the policy is NLA_STRING, not NLA_NUL_STRING, so a payload without the trailing NUL is legit input here.

Skip the kmalloc/nla_strscpy dance entirely and use nla_strdup(), which already allocates srclen + 1 and terminates. The nla_len() bounds checks stay as they were.

---
 kernel/taskstats.c |    3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)
kernel/taskstats.c +1 -2
@@ -368,10 +368,9 @@ static int parse(struct nlattr *na, stru
 		return -E2BIG;
 	if (len < 1)
 		return -EINVAL;
-	data = kmalloc(len, GFP_KERNEL);
+	data = nla_strdup(na, GFP_KERNEL);
 	if (!data)
 		return -ENOMEM;
-	nla_strscpy(data, na, len);
 	ret = cpulist_parse(data, mask);
 	kfree(data);
 	return ret;
author maintainer
#714 lore raw

PATCH 708/713 timekeeping: Check the return value of tk_get_aux_ts64 in __do_adjtimex()

In reply to Greg Kroah-Hartman

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

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

From: Thomas Weißschuh (Schneider Electric) <[email protected]>

commit 4b61084b11bcecce86d03804ff30f8d7b465593c upstream.

If the auxiliary clock is disabled during tk_get_aux_ts64() but is enabled before tks->clock_valid is checked, then uninitialized stackdata will be used in the calculations and indirectly leaked to userspace.

The same race window also exists after this change and also for the core timekeeper. But in these cases the only effect would be incorrect adjustments and this is userspace's responsibility to avoid this.

---
 kernel/time/timekeeping.c |    8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)
kernel/time/timekeeping.c +5 -3
@@ -2943,10 +2943,12 @@ static int __do_adjtimex(struct tk_data
 		return ret;
 	add_device_randomness(txc, sizeof(*txc));
 
-	if (!aux_clock)
+	if (!aux_clock) {
 		ktime_get_real_ts64(&ts);
-	else
-		tk_get_aux_ts64(tkd->timekeeper.id, &ts);
+	} else {
+		if (!tk_get_aux_ts64(tkd->timekeeper.id, &ts))
+			return -ENODEV;
+	}
 
 	add_device_randomness(&ts, sizeof(ts));
 
author maintainer
#715 lore raw

PATCH 709/713 timer: Keep debugobjects state consistent in migrate_timer_list()

In reply to Greg Kroah-Hartman

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

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

From: Thomas Gleixner <[email protected]>

commit c793bbfc4a0a9f5a66978fc91559e9681748dbeb upstream.

When timers are migrated away from an offline CPU the debugobjects state gets corrupted. The timer is accounted as inactive on deletion, but the enqueue on the alive CPU lacks the activation call.

That used to work, but got broken when the trace point and the debug objects call got separated. That change missed to fixup migrate_timer_list().

Add the missing debug_timer_activate() invocation to fix it.

Fixes dc1e7dc5ac62 ("timer: Move trace point to get proper index")
---
 kernel/time/timer.c |    1 +
 1 file changed, 1 insertion(+)
kernel/time/timer.c +1 -0
@@ -2492,6 +2492,7 @@ static void migrate_timer_list(struct ti
 		timer = hlist_entry(head->first, struct timer_list, entry);
 		detach_timer(timer, false);
 		timer->flags = (timer->flags & ~TIMER_BASEMASK) | cpu;
+		debug_timer_activate(timer);
 		internal_add_timer(new_base, timer);
 	}
 }
author maintainer
#716 lore raw

PATCH 710/713 udf: Fix i_lenExtents truncation on 32-bit kernels

In reply to Greg Kroah-Hartman

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

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

From: Zhan Xusheng <[email protected]>

commit a5a5ed23b1340ff0f32a14a7ca8585f7c4e9b2e2 upstream.

In udf_do_extend_file() the total extent length is rounded up to a block boundary with:

	iinfo->i_lenExtents = (iinfo->i_lenExtents + sb->s_blocksize - 1) &
			      ~(sb->s_blocksize - 1);

i_lenExtents is a __u64, but sb->s_blocksize is unsigned long. On 32-bit kernels unsigned long is 32-bit, so ~(sb->s_blocksize - 1) is a 32-bit value (e.g. 0xfffff800 for a 2 KiB block) that is zero-extended in the AND, clearing the upper 32 bits of i_lenExtents. For UDF files whose total extent length exceeds 4 GiB this truncates i_lenExtents when the file is extended, corrupting the tracked extent length.

Cast the block size to 64-bit before forming the mask. 64-bit kernels are unaffected.

---
 fs/udf/inode.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
fs/udf/inode.c +1 -1
@@ -531,7 +531,7 @@ static int udf_do_extend_file(struct ino
 			  sb->s_blocksize - 1) & ~(sb->s_blocksize - 1));
 		iinfo->i_lenExtents =
 			(iinfo->i_lenExtents + sb->s_blocksize - 1) &
-			~(sb->s_blocksize - 1);
+			~((u64)sb->s_blocksize - 1);
 	}
 
 	add = 0;
author maintainer
#717 lore raw

PATCH 711/713 selftests/mm: fix on-fault-limit false failure under sudo-rs

In reply to Greg Kroah-Hartman

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

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

From: Injae Ryou <[email protected]>

commit 98df164036bed307a16e7c124ad023c2c13c4b76 upstream.

run_vmtests.sh runs on-fault-limit as the nobody user via "sudo -u nobody ./on-fault-limit", guarded by a check that nobody can access the binary ("sudo -u nobody ls ./on-fault-limit").

The guard resolves the relative path from the inherited working directory, which only requires search permission on the test directory itself. Classic sudo passes the relative path through to execve() the same way, so the two agree. However, sudo-rs (the default sudo implementation since Ubuntu 25.10) canonicalizes the command to an absolute path before executing it, which requires search permission on every ancestor directory. When the kernel tree lives under a private home directory (mode 0750, the Ubuntu default for new users since 21.04), the guard passes but the execution fails with "command not found", and the test is reported as a false FAIL:

  # running sudo -u nobody ./on-fault-limit
  sudo: './on-fault-limit': command not found
  # [FAIL]

Wrap the command in "sh -c" so that sudo only resolves the shell binary, and the relative path is resolved by nobody's shell from the inherited working directory, matching what the guard checks. This is the only "sudo -u nobody" invocation in the script; uid, cwd, rlimits (including RLIMIT_MEMLOCK, which this test exercises) and the exit status are unchanged through sh.

Verified on Ubuntu 26.04 (sudo-rs 0.2.13): the test now runs and passes instead of failing. Verified on Ubuntu 24.04 (sudo 1.9.15p5): behavior is unchanged.

Fixes 5d2146a3354f ("selftests/mm: skip mlock tests if nobody user can't read it")
Cc Brendan Jackman <[email protected]>
Cc Brendan Jackman <[email protected]>
Cc David Hildenbrand <[email protected]>
Cc Lorenzo Stoakes <[email protected]>
---
 tools/testing/selftests/mm/run_vmtests.sh |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
tools/testing/selftests/mm/run_vmtests.sh +1 -1
@@ -303,7 +303,7 @@ CATEGORY="compaction" run_test ./compact
 
 if command -v sudo &> /dev/null && sudo -u nobody ls ./on-fault-limit >/dev/null;
 then
-	CATEGORY="mlock" run_test sudo -u nobody ./on-fault-limit
+	CATEGORY="mlock" run_test sudo -u nobody sh -c ./on-fault-limit
 else
 	echo "# SKIP ./on-fault-limit"
 fi
author maintainer
#718 lore raw

PATCH 712/713 ACPI: scan: Do not combine resources that overlap completely

In reply to Greg Kroah-Hartman

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

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

From: Rafael J. Wysocki <[email protected]>

commit 7617cc05df28dcae967cca109de74084321eaa62 upstream.

Commit f234fdaae1ca ("ACPI: scan: Avoid registering platform devices with resource overlaps") attempted to avoid platform device registration errors due to overlaps of resources of the same type returned by the same _CRS object in the ACPI tables. It did that by combining two or more overlapping resources into one, but it went too far and also caused resources that overlap completely to be combined which broke the arm-cmn driver that expects two MMIO resources to be present for each device it binds to and it expects those two resources to overlap completely.

Address this issue by adding checks for completely overlapping resources to acpi_platform_adjust_resources() and add a comment explaining what is done there.

---
 drivers/acpi/acpi_platform.c |    8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)
drivers/acpi/acpi_platform.c +7 -1
@@ -85,7 +85,13 @@ static unsigned int acpi_platform_adjust
 	for (i = 0; i < count; ) {
 		struct resource *res = &resources[i];
 
-		if (resource_type(new_res) != resource_type(res) ||
+		/*
+		 * Look for overlaps of resources of the same type that would
+		 * cause resource insertion to fail down the road.
+		 */
+		if (__resource_contains_unbound(res, new_res) ||
+		    __resource_contains_unbound(new_res, res) ||
+		    resource_type(new_res) != resource_type(res) ||
 		    !resource_union(new_res, res, new_res)) {
 			i++;
 			continue;
author maintainer
#719 lore raw

PATCH 713/713 platform/chrome: sensorhub: Fix dropped timestamp events and log spam

In reply to Greg Kroah-Hartman

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

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

From: Tzung-Bi Shih <[email protected]>

commit 9a3f43b30373c61477d0d3ab52946c05f9492bf9 upstream.

Commit 833740a2333c ("platform/chrome: sensorhub: Bound the EC-reported sensor number") evaluated the sensor_num against the bounds limit even for timestamp events. A timestamp event typically has a sensor_num of 0xff [1], causing the driver to flag it as invalid and skip to the next event.

As a result, we'd see a flooding of "Invalid sensor number 255 from EC" warning logs and these timestamp events were being dropped.

Move the bounds-check into cros_ec_sensor_ring_process_event() and evaluate it only after standalone timestamp events have already been processed and returned early.

[1] https://crrev.com/219ca6ef82ba266da788b673ee4ad50bd3ea1285/common/motion_sense_fifo.c#427

Fixes 833740a2333c ("platform/chrome: sensorhub: Bound the EC-reported sensor number")
---
 drivers/platform/chrome/cros_ec_sensorhub_ring.c |   27 ++++++++++++-----------
 1 file changed, 15 insertions(+), 12 deletions(-)
drivers/platform/chrome/cros_ec_sensorhub_ring.c +15 -12
@@ -475,6 +475,21 @@ cros_ec_sensor_ring_process_event(struct
 						  fifo_timestamp,
 						  *current_timestamp,
 						  now);
+
+		/*
+		 * A standalone timestamp event typically has a sensor_num of
+		 * 0xff.  Return early here to prevent it from hitting the
+		 * bounds check below and spamming the logs.
+		 */
+		return false;
+	}
+
+	/* Skip event if sensor_num from EC is out of bounds. */
+	if (in->sensor_num >= sensorhub->sensor_num) {
+		dev_warn_ratelimited(sensorhub->dev,
+				     "Invalid sensor number %u from EC\n",
+				     in->sensor_num);
+		return false;
 	}
 
 	if (in->flags & MOTIONSENSE_SENSOR_FLAG_ODR) {
@@ -502,10 +517,6 @@ cros_ec_sensor_ring_process_event(struct
 		return true;
 	}
 
-	if (in->flags & MOTIONSENSE_SENSOR_FLAG_TIMESTAMP)
-		/* If we just have a timestamp, skip this entry. */
-		return false;
-
 	/* Regular sample */
 	out->sensor_id = in->sensor_num;
 	trace_cros_ec_sensorhub_data(in->sensor_num,
@@ -890,14 +901,6 @@ static void cros_ec_sensorhub_ring_handl
 
 		for (in = sensorhub->resp->fifo_read.data, j = 0;
 		     j < number_data; j++, in++) {
-			/* Skip event if sensor_num from EC is out of bounds. */
-			if (in->sensor_num >= sensorhub->sensor_num) {
-				dev_warn_ratelimited(sensorhub->dev,
-						     "Invalid sensor number %u from EC\n",
-						     in->sensor_num);
-				continue;
-			}
-
 			if (cros_ec_sensor_ring_process_event(
 						sensorhub, fifo_info,
 						fifo_timestamp,
Tested-by on the v1 series
#720 lore raw
In reply to Greg Kroah-Hartman

Hi Greg

Quoted 10 lines · Greg Kroah-Hartman

This is the start of the stable review cycle for the 7.2.4 release. There are 713 patches in this series, all will be posted as a response to this one. If anyone has any issues with these being applied, please let me know.

Responses should be made by Sun, 06 Sep 2026 04:56:59 +0000. Anything received after that time might be too late.

The whole patch series can be found in one patch at:

Quoted 8 lines
        https://www.kernel.org/pub/linux/kernel/v7.x/stable-review/patch-7.2.4-rc1.gz

or in the git tree and branch at:
git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable-rc.git linux-7.2.y
and the diffstat can be found below.

thanks,

greg k-h

Linux version 7.2.4-rc1 tested.

Build successfully completed.
Boot successfully completed.
No dmesg regressions.
Video output normal.
Sound output normal.

Lenovo ThinkPad X1 Carbon Gen10(Intel i7-1260P(x86_64) arch linux)

[    0.000000] Linux version 7.2.4-rc1rv-ga5fdedcba898

(takeshi@ThinkPadX1Gen10J0764) (gcc (GCC) 16.2.1 20260810, GNU ld (GNU Binutils) 2.47) #1 SMP PREEMPT_DYNAMIC Fri Sep 4 17:16:05 JST 2026 Thanks

Tested-by on the v1 series
#723 lore raw
In reply to Greg Kroah-Hartman
Quoted 7 lines · Greg Kroah-Hartman

This is the start of the stable review cycle for the 7.2.4 release. There are 713 patches in this series, all will be posted as a response to this one. If anyone has any issues with these being applied, please let me know.

Responses should be made by Sun, 06 Sep 2026 04:56:59 +0000. Anything received after that time might be too late.

7.2.4-rc1 compiles on x86_64 (Xeon E5-1620 v2, Slackware64-15.0), and boots & runs on x86_64 (AMD Ryzen 5 7520U, Slackware64-current). No regressions observed during boot & my custom tests.

Tested-by on the v1 series
#724 lore raw
In reply to Greg Kroah-Hartman
Quoted 9 lines · Greg Kroah-Hartman

This is the start of the stable review cycle for the 7.2.4 release. There are 713 patches in this series, all will be posted as a response to this one. If anyone has any issues with these being applied, please let me know.

Responses should be made by Sun, 06 Sep 2026 04:56:59 +0000. Anything received after that time might be too late.

The whole patch series can be found in one patch at:

Quoted 9 lines
	https://www.kernel.org/pub/linux/kernel/v7.x/stable-review/patch-7.2.4-rc1.gz

or in the git tree and branch at:
git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable-rc.git linux-7.2.y
and the diffstat can be found below.

thanks,

greg k-h

Compiled and booted on my test system. No dmesg regressions.

thanks,
-- Shuah

Tested-by on the v1 series
#725 lore raw
In reply to Greg Kroah-Hartman
Quoted 8 lines · On Fri, 04 Sep 2026 06:49:28 +0200 Greg Kroah-Hartman…

This is the start of the stable review cycle for the 7.2.4 release. There are 713 patches in this series, all will be posted as a response to this one. If anyone has any issues with these being applied, please let me know.

Responses should be made by Sun, 06 Sep 2026 04:56:59 +0000. Anything received after that time might be too late.

Boot-tested under QEMU for Rust x86_64, arm64 and riscv64; built-tested for loongarch64 and arm32:

Thanks!

Cheers,
Miguel

Tested-by on the v1 series
#726 lore raw
In reply to Greg Kroah-Hartman
Quoted 9 lines · Greg Kroah-Hartman

This is the start of the stable review cycle for the 7.2.4 release. There are 713 patches in this series, all will be posted as a response to this one. If anyone has any issues with these being applied, please let me know.

Responses should be made by Sun, 06 Sep 2026 04:56:59 +0000. Anything received after that time might be too late.

The whole patch series can be found in one patch at:

Quoted 8 lines
	https://www.kernel.org/pub/linux/kernel/v7.x/stable-review/patch-7.2.4-rc1.gz

or in the git tree and branch at:
git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable-rc.git linux-7.2.y
and the diffstat can be found below.

thanks,

greg k-h

Tested on 3 amd64 systems (my DIY home NAS, my Lenovo ThinkPad T14 Gen 1, and an Apple MacBook Air 13" 2017). Working well, no regressions observed.

Signature · 1 line
-Barry K. Nathan  <[email protected]>
Tested-by on the v1 series
#727 lore raw
In reply to Greg Kroah-Hartman

Am 04.09.2026 um 06:49 schrieb Greg Kroah-Hartman:

Quoted 4 lines

This is the start of the stable review cycle for the 7.2.4 release. There are 713 patches in this series, all will be posted as a response to this one. If anyone has any issues with these being applied, please let me know.

Builds, boots and works on my 2-socket Ivy Bridge Xeon E5-2697v2 server. No dmesg oddities or regressions found.

Beste Grüße,
Peter Schneider

Signature · 8 lines
Climb the mountain not to plant your flag, but to embrace the challenge,
enjoy the air and behold the view. Climb it so you can see the world,
not so the world can see you.                    -- David McCullough Jr.

OpenPGP:  0xA3828BD796CCE11A8CADE8866E3A92C92C3FF244
Download: https://www.peters-netzplatz.de/download/pschneider1968_pub.asc
https://keys.mailvelope.com/pks/lookup?op=get&[email protected]
https://keys.mailvelope.com/pks/lookup?op=get&[email protected]
Tested-by on the v1 series
#728 lore raw
In reply to Greg Kroah-Hartman
Quoted 9 lines · Greg Kroah-Hartman

This is the start of the stable review cycle for the 7.2.4 release. There are 713 patches in this series, all will be posted as a response to this one. If anyone has any issues with these being applied, please let me know.

Responses should be made by Sun, 06 Sep 2026 04:56:59 +0000. Anything received after that time might be too late.

The whole patch series can be found in one patch at:

Quoted 8 lines
	https://www.kernel.org/pub/linux/kernel/v7.x/stable-review/patch-7.2.4-rc1.gz

or in the git tree and branch at:
git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable-rc.git linux-7.2.y
and the diffstat can be found below.

thanks,

greg k-h

Built and booted successfully on RISC-V RV64 (HiFive Unmatched).

Tested-by on the v1 series
#729 lore raw
In reply to Greg Kroah-Hartman
Quoted 4 lines · Greg Kroah-Hartman

This is the start of the stable review cycle for the 7.2.4 release. There are 713 patches in this series, all will be posted as a response to this one. If anyone has any issues with these being applied, please let me know.

Build and boot tested with QEMU for x86_64, i386, arm64, and riscv across multiple configurations, and boots on AMD Ryzen 7 5800H. No regressions observed.