Skip to contentSkip to conversation

6.18.55-rc1 review

PATCH In review Greg Kroah-Hartman 9 participants 404 messages Sep 30 – Oct 2 Page 3 of 3

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

PATCH 279/395 net: openvswitch: conntrack: remove add_helper dead code

In reply to Greg Kroah-Hartman

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

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

From: Ilya Maximets <[email protected]>

commit 5e6c14dd42a1c1fe938e573dc6c9098145b2b0c4 upstream.

This variable can only become 'true' when the connection is not confirmed, but it is only checked when it is confirmed. So, it can be treated as being always false and just removed.

Fixes 3c1860543fcc ("openvswitch: add nf_ct_is_confirmed check before assigning the helper")
---
 net/openvswitch/conntrack.c |   10 ++--------
 1 file changed, 2 insertions(+), 8 deletions(-)
net/openvswitch/conntrack.c +2 -8
@@ -779,8 +779,6 @@ static int __ovs_ct_lookup(struct net *n
 
 	ct = nf_ct_get(skb, &ctinfo);
 	if (ct) {
-		bool add_helper = false;
-
 		/* Packets starting a new connection must be NATted before the
 		 * helper, so that the helper knows about the NAT.  We enforce
 		 * this by delaying both NAT and helper calls for unconfirmed
@@ -812,7 +810,6 @@ static int __ovs_ct_lookup(struct net *n
 							    GFP_ATOMIC);
 			if (err)
 				return err;
-			add_helper = true;
 
 			/* helper installed, add seqadj if NAT is required */
 			if (info->nat && !nfct_seqadj(ct)) {
@@ -822,13 +819,10 @@ static int __ovs_ct_lookup(struct net *n
 		}
 
 		/* Call the helper only if:
-		 * - nf_conntrack_in() was executed above ("!cached") or a
-		 *   helper was just attached ("add_helper") for a confirmed
-		 *   connection, or
+		 * - nf_conntrack_in() was executed above ("!cached"), or
 		 * - When committing an unconfirmed connection.
 		 */
-		if ((nf_ct_is_confirmed(ct) ? !cached || add_helper :
-					      info->commit)) {
+		if ((nf_ct_is_confirmed(ct) ? !cached : info->commit)) {
 			int err = nf_ct_helper(skb, ct, ctinfo, info->family);
 
 			err = verdict_to_errno(err);
author maintainer
#280 lore raw

PATCH 280/395 net: openvswitch: conntrack: fix helper UAF due to extensions realloc

In reply to Greg Kroah-Hartman

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

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

From: Ilya Maximets <[email protected]>

commit 1a4151e6be57b098b7a5ebfbde58585e83200cdc upstream.

While calling the helpers, a raw pointer to the extensions area is wired into expectations list:

-> nf_ct_helper()
-> helper->help()

    -> nf_ct_expect_related_report()
     -> nf_ct_expect_insert()
      -> hlist_add_head_rcu(&exp->lnode, &master_help->expectations)

In case the connection is not confirmed yet, more extensions can be added afterwards with *_ext_add() calls reallocating the extension space and leaving the now invalid pointer in the expectations list that is later accessed while removing the expectation.

Make sure that helpers are called at the end after all the other extensions are already added.

Note that the helper rejection now leaves the mark and labels set, but that's not different from how the NAT was handled before or how the mark and the labels were handled on confirmation failure. And there are no atomicity guarantees provided by the API anyway.

---
 net/openvswitch/conntrack.c |   19 +++++++++++++++----
 1 file changed, 15 insertions(+), 4 deletions(-)
net/openvswitch/conntrack.c +15 -4
@@ -818,11 +818,14 @@ static int __ovs_ct_lookup(struct net *n
 			}
 		}
 
-		/* Call the helper only if:
-		 * - nf_conntrack_in() was executed above ("!cached"), or
-		 * - When committing an unconfirmed connection.
+		/* Call the helper only if nf_conntrack_in() was executed
+		 * above ("!cached").
+		 *
+		 * For unconfirmed connections it will be called later during
+		 * commit as we need to have all the other extensions allocated
+		 * before the call.
 		 */
-		if ((nf_ct_is_confirmed(ct) ? !cached : info->commit)) {
+		if (nf_ct_is_confirmed(ct) && !cached) {
 			int err = nf_ct_helper(skb, ct, ctinfo, info->family);
 
 			err = verdict_to_errno(err);
@@ -1025,6 +1028,14 @@ static int ovs_ct_commit(struct net *net
 			return err;
 
 		nf_conn_act_ct_ext_add(skb, ct, ctinfo);
+
+		/* Call the helpers now.  We couldn't do this before as
+		 * all the extensions must be allocated before the call.
+		 */
+		err = nf_ct_helper(skb, ct, ctinfo, info->family);
+		err = verdict_to_errno(err);
+		if (err)
+			return err;
 	} else if (IS_ENABLED(CONFIG_NF_CONNTRACK_LABELS) &&
 		   labels_nonzero(&info->labels.mask)) {
 		err = ovs_ct_set_labels(ct, key, &info->labels.value,
author maintainer
#281 lore raw

PATCH 281/395 net: atl1c: fix soft lockup on out-of-range tpd_cons read

In reply to Greg Kroah-Hartman

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

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

From: Gajdos Tamás <[email protected]>

commit 36c2009d90f2210ef92e6f4f2850e8b57b09e754 upstream.

The hardware can report an out-of-range tpd_cons (seen as 0xffff) while the PCIe link/MAC is resetting. An out-of-range value can never be reached and the loop below would spin forever. To avoid a soft lockup treat it as "nothing new to clean" instead.

Reproduced on two machines, same NIC (Qualcomm Atheros AR8151 v2.0, 4-port), triggered by rebooting a Mikrotik CCR2004 PCIe card that the ports are directly linked to:

- Ubuntu 26.04.1 LTS, kernel 7.0.0-31-generic. The link-flap precursor, before the lockup was captured with a full trace elsewhere:

    atl1c 0000:05:00.0 enp5s0f0: NETDEV WATCHDOG: CPU: 4: transmit queue 2 timed out 489984 ms
    atl1c 0000:05:00.0: MAC state machine can't be idle since disabled for 10ms second
    atl1c 0000:05:00.0: atl1c: enp5s0f0 NIC Link is Up<65535 Mbps Full Duplex>

65535 (0xffff) here is the same value tpd_cons reads back once the loop below gets stuck.

- Proxmox VE, kernel 7.0.14-11-pve. Same NIC/trigger, this time caught by the soft lockup watchdog with a full stack trace:

    watchdog: BUG: soft lockup - CPU#12 stuck for 354s! [napi/eth%d-0:329]
    CPU: 12 UID: 0 PID: 329 Comm: napi/eth%d-0 Tainted: P O L 7.0.14-11-pve #1 PREEMPT(lazy)
    RIP: 0010:atl1c_clean_tx+0x142/0x2d0 [atl1c]
    Call Trace:
     <TASK>
     __napi_poll+0x32/0x1e0
     napi_threaded_poll_loop+0x286/0x2e0
     napi_threaded_poll+0xfd/0x140
     kthread+0xf7/0x130
     ret_from_fork+0x2da/0x3a0
     ret_from_fork_asm+0x1a/0x30
     </TASK>
Fixes 43250ddd75a35d ("atl1c: Atheros L1C Gigabit Ethernet driver")
---
 drivers/net/ethernet/atheros/atl1c/atl1c_main.c |    3 +++
 1 file changed, 3 insertions(+)
drivers/net/ethernet/atheros/atl1c/atl1c_main.c +3 -0
@@ -1602,6 +1602,9 @@ static int atl1c_clean_tx(struct napi_st
 	AT_READ_REGW(&adapter->hw, atl1c_qregs[tpd_ring->num].tpd_cons,
 		     &hw_next_to_clean);
 
+	if (unlikely(hw_next_to_clean >= tpd_ring->count))
+		hw_next_to_clean = next_to_clean;
+
 	while (next_to_clean != hw_next_to_clean) {
 		buffer_info = &tpd_ring->buffer_info[next_to_clean];
 		if (buffer_info->skb) {
author maintainer
#282 lore raw

PATCH 282/395 net: atl1e: fix soft lockup on out-of-range hw_next_to_clean read

In reply to Greg Kroah-Hartman

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

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

From: Gajdos Tamás <[email protected]>

commit 374bf9e4b90f979e052332c4faca2d745c491a12 upstream.

Same issue as atl1c (see the first commit in this series, "net:
atl1c: fix soft lockup on out-of-range tpd_cons read"): the hardware can report an out-of-range hw_next_to_clean (seen as 0xffff) while the PCIe link/MAC is resetting. An out-of-range value can never be reached and the loop below would spin forever. Treat it as "nothing new to clean" instead.

Fixes a6a5325239c202 ("atl1e: Atheros L1E Gigabit Ethernet driver")
---
 drivers/net/ethernet/atheros/atl1e/atl1e_main.c |    3 +++
 1 file changed, 3 insertions(+)
drivers/net/ethernet/atheros/atl1e/atl1e_main.c +3 -0
@@ -1234,6 +1234,9 @@ static bool atl1e_clean_tx_irq(struct at
 	u16 hw_next_to_clean = AT_READ_REGW(&adapter->hw, REG_TPD_CONS_IDX);
 	u16 next_to_clean = atomic_read(&tx_ring->next_to_clean);
 
+	if (unlikely(hw_next_to_clean >= tx_ring->count))
+		hw_next_to_clean = next_to_clean;
+
 	while (next_to_clean != hw_next_to_clean) {
 		tx_buffer = &tx_ring->tx_buffer[next_to_clean];
 		if (tx_buffer->dma) {
author maintainer
#283 lore raw

PATCH 283/395 net: bridge: mdb: restart port group walk after deletion

In reply to Greg Kroah-Hartman

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

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

From: Fourie Zhang <[email protected]>

commit ab1404ac81154a89fb61ac50ae9a04cd8d4834dc upstream.

br_mdb_flush_pgs() keeps a pointer-to-pointer cursor while walking mp->ports. br_multicast_del_pg() can re-enter the same MDB entry through br_multicast_sg_del_exclude_ports() and unlink other port groups. If the cursor points into one of those groups, the next iteration dereferences a stale cursor and can leave mp->ports pointing at freed memory.

A following RTM_GETMDB exposes the dangling pointer:

  BUG: KASAN: slab-use-after-free in br_mdb_dump
  Read of size 8
    br_mdb_dump
    rtnl_mdb_dump
    rtnl_dumpit
    netlink_dump

Reset the cursor to mp->ports after every deletion. The deletion removes at least the selected group, so the restarted walk always makes progress.

---
 net/bridge/br_mdb.c |    2 ++
 1 file changed, 2 insertions(+)
net/bridge/br_mdb.c +2 -0
@@ -1523,6 +1523,8 @@ static void br_mdb_flush_pgs(struct net_
 		}
 
 		br_multicast_del_pg(mp, p, pp);
+		/* br_multicast_del_pg() can remove other groups from this list. */
+		pp = &mp->ports;
 	}
 }
 
author maintainer
#284 lore raw

PATCH 284/395 net: ena: fix PHC cleanup on probe failure

In reply to Greg Kroah-Hartman

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

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

From: Guangshuo Li <[email protected]>

commit 0958ea4355e2e9220ad4e13da3b7d94f365ed34f upstream.

ena_probe() initializes the PHC as part of ena_device_init(), but the probe failure path does not destroy it before freeing the PHC private data.

The normal removal path calls ena_phc_destroy() through ena_destroy_device() before ena_phc_free(). However, if probe fails after ena_device_init() succeeds, the error path reaches ena_phc_free() without unregistering the PTP clock or destroying the device PHC resources.

Call ena_phc_destroy() in the probe error path before freeing the PHC private data.

This issue was found by manual code inspection.

Cc [email protected] tags and describe this as a consistency cleanup
Fixes e0ea34158ee8 ("net: ena: Add PHC support in the ENA driver")
Cc stable
---
 drivers/net/ethernet/amazon/ena/ena_netdev.c |    1 +
 1 file changed, 1 insertion(+)
drivers/net/ethernet/amazon/ena/ena_netdev.c +1 -0
@@ -4126,6 +4126,7 @@ err_worker_destroy:
 err_device_destroy:
 	ena_com_delete_host_info(ena_dev);
 	ena_com_admin_destroy(ena_dev);
+	ena_phc_destroy(adapter);
 ena_devlink_destroy:
 	ena_devlink_free(devlink);
 err_metrics_destroy:
author maintainer
#285 lore raw

PATCH 285/395 net: ena: fix MMIO read buffer leak on probe failure

In reply to Greg Kroah-Hartman

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

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

From: Guangshuo Li <[email protected]>

commit 9476b4468862927297c94c440863cd8ed1e7cc83 upstream.

ena_device_init() initializes the MMIO read mechanism with ena_com_mmio_reg_read_request_init(), which allocates a coherent DMA buffer for MMIO read responses.

The normal removal path releases this buffer through ena_com_mmio_reg_read_request_destroy(). However, if ena_probe() fails after ena_device_init() succeeds, the error path destroys the admin resources and eventually frees ena_dev without destroying the MMIO read request, leaving the coherent DMA buffer allocated.

Call ena_com_mmio_reg_read_request_destroy() in the probe error path before releasing the remaining device resources.

This issue was found by manual code inspection.

Fixes 1738cd3ed342 ("net: ena: Add a driver for Amazon Elastic Network Adapters (ENA)")
---
 drivers/net/ethernet/amazon/ena/ena_netdev.c |    1 +
 1 file changed, 1 insertion(+)
drivers/net/ethernet/amazon/ena/ena_netdev.c +1 -0
@@ -4127,6 +4127,7 @@ err_device_destroy:
 	ena_com_delete_host_info(ena_dev);
 	ena_com_admin_destroy(ena_dev);
 	ena_phc_destroy(adapter);
+	ena_com_mmio_reg_read_request_destroy(ena_dev);
 ena_devlink_destroy:
 	ena_devlink_free(devlink);
 err_metrics_destroy:
author maintainer
#286 lore raw

PATCH 286/395 net: phy: micrel: Advance register data pointer in write loop

In reply to Greg Kroah-Hartman

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

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

From: Abhishek Ojha <[email protected]>

commit 95c4d54ed02283e9a09e8cd7360e384daa67a741 upstream.

lanphy_write_reg_data() does not advance the data pointer while iterating over the register table. As a result, it writes the first entry num times and leaves the remaining errata registers unconfigured.

Single-entry tables are unaffected, but tables with multiple entries leave every entry after the first unapplied.

Advance the data pointer after each successful write so every table entry is applied in order.

---
 drivers/net/phy/micrel.c |    1 +
 1 file changed, 1 insertion(+)
drivers/net/phy/micrel.c +1 -0
@@ -6034,6 +6034,7 @@ static int lanphy_write_reg_data(struct
 					    data->val);
 		if (ret)
 			break;
+		data++;
 	}
 
 	return ret;
author maintainer
#287 lore raw

PATCH 287/395 net: txgbe: fix FDIR filter restore for VF rules

In reply to Greg Kroah-Hartman

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

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

From: Zhang Yunfei <[email protected]>

commit 651010592bdce7005c1179498327e51bfc4fe1a5 upstream.

txgbe_fdir_filter_restore() reprograms every filter from txgbe->fdir_filter_list after a reset. It extracts the ring part of filter->action with ethtool_get_flow_spec_ring() and maps it onto a PF rx ring, silently dropping the VF part of the cookie that txgbe_add_ethtool_fdir_entry() stores there (input->action = fsp->ring_cookie).

For a rule directed at a VF, restore therefore reprograms the filter to the PF queue with the same ring index: after any down/up or txgbe_reinit_locked(), traffic matching the rule is steered to the PF instead of the VF.

Handle VF rules the same way txgbe_add_ethtool_fdir_entry() does:
validate vf against wx->num_vfs and ring against wx->num_rx_queues_per_pool, and map the ring onto the absolute queue index ((vf - 1) * wx->num_rx_queues_per_pool) + ring.

Fixes 7a91722e0dd4 ("net: txgbe: Support the FDIR rules assigned to VFs")
---
 drivers/net/ethernet/wangxun/txgbe/txgbe_fdir.c |   13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)
drivers/net/ethernet/wangxun/txgbe/txgbe_fdir.c +11 -2
@@ -591,15 +591,24 @@ static void txgbe_fdir_filter_restore(st
 			queue = TXGBE_RDB_FDIR_DROP_QUEUE;
 		} else {
 			u32 ring = ethtool_get_flow_spec_ring(filter->action);
+			u8 vf = ethtool_get_flow_spec_ring_vf(filter->action);
 
-			if (ring >= wx->num_rx_queues) {
+			if (!vf && ring >= wx->num_rx_queues) {
 				wx_err(wx, "FDIR restore failed, ring:%u\n",
 				       ring);
 				continue;
+			} else if (vf && (vf > wx->num_vfs ||
+				  ring >= wx->num_rx_queues_per_pool)) {
+				wx_err(wx, "FDIR restore failed, vf:%u, ring:%u\n",
+				       vf, ring);
+				continue;
 			}
 
 			/* Map the ring onto the absolute queue index */
-			queue = wx->rx_ring[ring]->reg_idx;
+			if (!vf)
+				queue = wx->rx_ring[ring]->reg_idx;
+			else
+				queue = ((vf - 1) * wx->num_rx_queues_per_pool) + ring;
 		}
 
 		ret = txgbe_fdir_write_perfect_filter(wx,
author maintainer
#288 lore raw

PATCH 288/395 net: usb: cdc_mbim: add MeiG Smart SRM821 to ZLP whitelist

In reply to Greg Kroah-Hartman

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

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

From: Ming Wang <[email protected]>

commit f75f21ef36285e5f56ee0c428bd2909ee81165b9 upstream.

The MeiG Smart SRM821 5G module (0x2dee:0x4d53) crashes and drops off the USB bus when it receives a Zero Length Packet (ZLP) after sending or receiving an NTB of exactly 16384 bytes (tx_max).

According to the MBIM specification, devices do not require a ZLP if the NTB size is exactly dwNtbOutMaxSize. However, the cdc_mbim driver defaults to sending ZLPs for devices not explicitly whitelisted to accommodate non-conformant hardware. This default behavior breaks the strictly conformant MeiG SRM821 module.

Add this device to the ZLP conformance whitelist (cdc_mbim_info) so the driver will pad the NTB to avoid sending ZLPs, preventing the device firmware from crashing.

---
 drivers/net/usb/cdc_mbim.c |    5 +++++
 1 file changed, 5 insertions(+)
drivers/net/usb/cdc_mbim.c +5 -0
@@ -634,6 +634,11 @@ static const struct usb_device_id mbim_d
 	  .driver_info = (unsigned long)&cdc_mbim_info,
 	},
 
+	/* MeiG Smart SRM821 ZLP conformance */
+	{ USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d53, USB_CLASS_COMM, USB_CDC_SUBCLASS_MBIM, USB_CDC_PROTO_NONE),
+	  .driver_info = (unsigned long)&cdc_mbim_info,
+	},
+
 	/* Some Huawei devices, ME906s-158 (12d1:15c1) and E3372
 	 * (12d1:157d), are known to fail unless the NDP is placed
 	 * after the IP packets.  Applying the quirk to all Huawei
author maintainer
#289 lore raw

PATCH 289/395 net: usb: lan78xx: Fix URB reference leak in lan78xx_submit_deferred_urbs()

In reply to Greg Kroah-Hartman

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

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

From: Wentao Liang <[email protected]>

commit 17741334d00bf5ebd37f8c1c36bc9c146a351deb upstream.

usb_get_from_anchor() hands over a reference to the URB, which the caller must release. lan78xx_submit_deferred_urbs() never does, so every deferred Tx URB keeps an extra reference: the counter grows on each suspend/resume cycle and the URBs are never freed when the buffers are released. Drop the reference after submitting, and on the path that drops the packet instead of submitting it.

Fixes 5f4cc6e25148 ("lan78xx: Fix race conditions in suspend/resume handling")
---
 drivers/net/usb/lan78xx.c |    2 ++
 1 file changed, 2 insertions(+)
drivers/net/usb/lan78xx.c +2 -0
@@ -5246,10 +5246,12 @@ static bool lan78xx_submit_deferred_urbs
 		    !netif_carrier_ok(dev->net) ||
 		    pipe_halted) {
 			lan78xx_release_tx_buf(dev, skb);
+			usb_put_urb(urb);
 			continue;
 		}
 
 		ret = usb_submit_urb(urb, GFP_ATOMIC);
+		usb_put_urb(urb);
 
 		if (ret == 0) {
 			netif_trans_update(dev->net);
author maintainer
#290 lore raw

PATCH 290/395 netfilter: ip6t_rpfilter: reject routes without inet6_dev

In reply to Greg Kroah-Hartman

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

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

From: Weiming Shi <[email protected]>

commit 1b9b5323725e458906c7620a3bc10398b51ad954 upstream.

ip6_route_lookup() can return an error-free route whose rt6i_idev is NULL. Lowering an external nexthop device's MTU below IPV6_MIN_MTU tears down its inet6_dev while fib6_ifdown() leaves routes using nexthop objects in the FIB. An unprivileged user can construct this state with rtnetlink in a private user and network namespace, then trigger a NULL dereference through an IPv6 rpfilter lookup:

  Oops: general protection fault, probably for non-canonical address
  0xdffffc0000000000
  KASAN: null-ptr-deref in range [0x0000000000000000-0x0000000000000007]
  RIP: rpfilter_mt (net/ipv6/netfilter/ip6t_rpfilter.c:75)
  Call Trace:
  ip6t_do_table (net/ipv6/netfilter/ip6_tables.c:316)
  nf_hook_slow (net/netfilter/core.c:619)
  ipv6_rcv (net/ipv6/ip6_input.c:351)
  __netif_receive_skb_one_core (net/core/dev.c:6216)
  process_backlog (net/core/dev.c:6680)
  __napi_poll (net/core/dev.c:7739)
  net_rx_action (net/core/dev.c:7959)
  handle_softirqs (kernel/softirq.c:622)
  do_softirq.part.0 (kernel/softirq.c:523)
  __local_bh_enable_ip (kernel/softirq.c:450)
  __dev_queue_xmit (net/core/dev.c:4913)
  packet_sendmsg (net/packet/af_packet.c:3139)
  __sys_sendto (net/socket.c:2252)
  __x64_sys_sendto (net/socket.c:2259)
  do_syscall_64 (arch/x86/entry/syscall_64.c:94)
  entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121)
  Kernel panic - not syncing: Fatal exception in interrupt

Reject routes without an inet6_dev immediately after lookup. Such routes are not eligible for reverse-path filtering, and the check protects all later rt6i_idev dereferences.

Fixes e26f9a480fb6 ("netfilter: add ipv6 reverse path filter match")
Reported-by [email protected]
Assisted-by Claude:gpt-5
---
 net/ipv6/netfilter/ip6t_rpfilter.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
net/ipv6/netfilter/ip6t_rpfilter.c +1 -1
@@ -61,7 +61,7 @@ static bool rpfilter_lookup_reverse6(str
 		fl6.flowi6_oif = dev->ifindex;
 
 	rt = (void *)ip6_route_lookup(net, &fl6, skb, lookup_flags);
-	if (rt->dst.error)
+	if (rt->dst.error || !rt->rt6i_idev)
 		goto out;
 
 	if (rt->rt6i_flags & (RTF_REJECT|RTF_ANYCAST))
author maintainer
#291 lore raw

PATCH 291/395 netfilter: ip6t_rt: fix zero-address non-strict match out-of-bounds read

In reply to Greg Kroah-Hartman

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

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

From: Luxiao Xu <[email protected]>

commit 82313c169eddc02b1bf5ba6b427803e272d3ec42 upstream.

rt_mt6_check() permits rules to be configured with rtinfo->addrnr == 0 even when address matching (IP6T_RT_FST_MASK) is requested.

In the IP6T_RT_FST_NSTRICT path, rt_mt6() evaluates packet routing addresses against rtinfo->addrs[i] and terminates backwards at the bottom of the loop:

    if (ipv6_addr_equal(ap, &rtinfo->addrs[i])) {
        i++;
    }
    if (i == rtinfo->addrnr)
        break;

When addrnr is 0, if the first packet address matches rtinfo->addrs[0], i is incremented to 1. Because i is now strictly greater than addrnr (0), the loop termination condition (i == rtinfo->addrnr) is bypassed and will never be satisfied.

If a crafted IPv6 packet contains matching routing addresses, i will advance past IP6T_RT_HOPS (16). The subsequent call to ipv6_addr_equal() reads beyond struct ip6t_rt, triggering UBSAN/KASAN out-of-bounds warnings or kernel panics.

Fix this by:
1. Rejecting rules in rt_mt6_check() where IP6T_RT_FST_MASK is set but rtinfo->addrnr is zero.
2. In rt_mt6(), moving the termination condition (i < rtinfo->addrnr) into the for-loop header condition and removing the backwards break at the end of the loop body.

---
 net/ipv6/netfilter/ip6t_rt.c |   11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)
net/ipv6/netfilter/ip6t_rt.c +8 -3
@@ -96,7 +96,8 @@ static bool rt_mt6(const struct sk_buff
 			unsigned int i = 0;
 
 			for (temp = 0;
-			     temp < (unsigned int)((hdrlen - 8) / 16);
+			     temp < (unsigned int)((hdrlen - 8) / 16) &&
+			     i < rtinfo->addrnr;
 			     temp++) {
 				ap = skb_header_pointer(skb,
 							ptr
@@ -112,8 +113,6 @@ static bool rt_mt6(const struct sk_buff
 
 				if (ipv6_addr_equal(ap, &rtinfo->addrs[i]))
 					i++;
-				if (i == rtinfo->addrnr)
-					break;
 			}
 			if (i == rtinfo->addrnr)
 				return ret;
@@ -162,6 +161,12 @@ static int rt_mt6_check(const struct xt_
 		pr_debug("too many addresses specified\n");
 		return -EINVAL;
 	}
+
+	if ((rtinfo->flags & IP6T_RT_FST_MASK) && !rtinfo->addrnr) {
+		pr_info_ratelimited("address list match requested but addrnr is 0\n");
+		return -EINVAL;
+	}
+
 	if ((rtinfo->flags & (IP6T_RT_RES | IP6T_RT_FST_MASK)) &&
 	    (!(rtinfo->flags & IP6T_RT_TYP) ||
 	     (rtinfo->rt_type != 0) ||
author maintainer
#292 lore raw

PATCH 292/395 netfilter: nf_tables: skip expired catchall elements on insert and delete

In reply to Greg Kroah-Hartman

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

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

From: Aohan Mei <[email protected]>

commit 70194dc37670bd08e44b471389861cc01bd3a3c9 upstream.

nft_setelem_catchall_insert() looks up duplicates with nft_set_elem_active() only, while nft_set_catchall_lookup() and the dump path additionally skip expired elements.

Once a catchall element with a timeout expires, this predicate drift makes it invisible to userspace dumps, yet it still blocks
re-insertion: with NLM_F_EXCL the request fails with -EEXIST, and without it the request reports success but silently inserts nothing. The stale entry only goes away when the (user-tunable) gc interval elapses, so the catchall rule may silently stop matching for an arbitrarily long time after its first expiration.

The delete path shows the same drift: nft_setelem_catchall_deactivate() picks the first active-next entry in the catchall list, so with an expired entry still pending GC it retires the stale entry instead of the fresh one, and it deactivates an element that userspace no longer sees instead of failing with -ENOENT.

Align both walks with the lookup and dump predicates: only an element that is active and not expired counts as a duplicate or delete candidate, using the per-netns timestamp taken at transaction start, in line with the set backend .insert/.deactivate and catchall GC sync paths.

Fixes aaa31047a6d2 ("netfilter: nftables: add catch-all set element support")
Assisted-by CodeBuddy:Kimi-K3
---
 net/netfilter/nf_tables_api.c |   10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)
net/netfilter/nf_tables_api.c +8 -2
@@ -6886,11 +6886,14 @@ static int nft_setelem_catchall_insert(c
 {
 	struct nft_set_elem_catchall *catchall;
 	u8 genmask = nft_genmask_next(net);
+	u64 tstamp = nft_net_tstamp(net);
 	struct nft_set_ext *ext;
 
 	list_for_each_entry(catchall, &set->catchall_list, list) {
 		ext = nft_set_elem_ext(set, catchall->elem);
-		if (nft_set_elem_active(ext, genmask)) {
+		if (nft_set_elem_active(ext, genmask) &&
+		    !__nft_set_elem_expired(ext, tstamp) &&
+		    !nft_set_elem_is_dead(ext)) {
 			*priv = catchall->elem;
 			return -EEXIST;
 		}
@@ -6983,11 +6986,14 @@ static int nft_setelem_catchall_deactiva
 					   struct nft_set_elem *elem)
 {
 	struct nft_set_elem_catchall *catchall;
+	u64 tstamp = nft_net_tstamp(net);
 	struct nft_set_ext *ext;
 
 	list_for_each_entry(catchall, &set->catchall_list, list) {
 		ext = nft_set_elem_ext(set, catchall->elem);
-		if (!nft_is_active_next(net, ext))
+		if (!nft_is_active_next(net, ext) ||
+		    __nft_set_elem_expired(ext, tstamp) ||
+		    nft_set_elem_is_dead(ext))
 			continue;
 
 		kfree(elem->priv);
author maintainer
#293 lore raw

PATCH 293/395 nfc: fix use-after-free in nfc_get_local_general_bytes

In reply to Greg Kroah-Hartman

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

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

From: Luxiao Xu <[email protected]>

commit dcab71a7011918f6fdba7adcec02d217dcb84b8d upstream.

Commit 6709d4b7bc2e ("net: nfc: Fix use-after-free caused by nfc_llcp_find_local") attempted to fix a use-after-free (UAF) issue by invoking nfc_llcp_local_put(local) after accessing local->gb. However, if the reference count drops to zero, local is freed immediately, leading to a use-after-free when callers access the returned pointer. Alternative approaches using dynamic allocation (e.g. kmemdup) introduced memory leaks because callers consistently treat the returned pointer as borrowed memory.

Fix this properly by refactoring nfc_llcp_general_bytes() and nfc_get_local_general_bytes() to accept a caller-provided output buffer (out_gb) and its maximum length (gb_max_len). The general bytes are safely copied into out_gb before calling nfc_llcp_local_put(local), ensuring safe lifetime management without ownership transfer complications.

Update all callers across drivers (microread, pn533, pn544, st21nfca, digital_dep, and nci) to provide their own destination buffers and pass them to nfc_get_local_general_bytes().

---
 drivers/nfc/microread/microread.c |    6 +++---
 drivers/nfc/pn533/pn533.c         |   14 ++++++++------
 drivers/nfc/pn533/pn533.h         |    4 +++-
 drivers/nfc/pn544/pn544.c         |    7 +++----
 drivers/nfc/st21nfca/core.c       |    8 ++++----
 include/net/nfc/hci.h             |    2 +-
 include/net/nfc/nfc.h             |    3 ++-
 net/nfc/core.c                    |   15 +++++++--------
 net/nfc/digital_dep.c             |    8 ++++----
 net/nfc/llcp_core.c               |   17 +++++++++++++----
 net/nfc/nci/core.c                |   10 +++++-----
 net/nfc/nfc.h                     |    3 ++-
 12 files changed, 55 insertions(+), 42 deletions(-)
drivers/nfc/microread/microread.c +3 -3
@@ -251,9 +251,9 @@ static int microread_start_poll(struct n
 		param[1] |= (1 << 1);
 
 	if ((im_protocols | tm_protocols) & NFC_PROTO_NFC_DEP_MASK) {
-		hdev->gb = nfc_get_local_general_bytes(hdev->ndev,
-						       &hdev->gb_len);
-		if (hdev->gb == NULL || hdev->gb_len == 0) {
+		nfc_get_local_general_bytes(hdev->ndev, hdev->gb,
+					    sizeof(hdev->gb), &hdev->gb_len);
+		if (hdev->gb_len == 0) {
 			im_protocols &= ~NFC_PROTO_NFC_DEP_MASK;
 			tm_protocols &= ~NFC_PROTO_NFC_DEP_MASK;
 		}
drivers/nfc/pn533/pn533.c +8 -6
@@ -1355,10 +1355,11 @@ static int pn533_poll_dep(struct nfc_dev
 	u8 *next, nfcid3[NFC_NFCID3_MAXSIZE];
 	u8 passive_data[PASSIVE_DATA_LEN] = {0x00, 0xff, 0xff, 0x00, 0x3};
 
-	if (!dev->gb) {
-		dev->gb = nfc_get_local_general_bytes(nfc_dev, &dev->gb_len);
-
-		if (!dev->gb || !dev->gb_len) {
+	if (!dev->gb_len) {
+		nfc_get_local_general_bytes(nfc_dev, dev->gb,
+					    sizeof(dev->gb),
+					    &dev->gb_len);
+		if (!dev->gb_len) {
 			dev->poll_dep = 0;
 			queue_work(dev->wq, &dev->rf_work);
 		}
@@ -1656,8 +1657,9 @@ static int pn533_start_poll(struct nfc_d
 	}
 
 	if (tm_protocols) {
-		dev->gb = nfc_get_local_general_bytes(nfc_dev, &dev->gb_len);
-		if (dev->gb == NULL)
+		nfc_get_local_general_bytes(nfc_dev, dev->gb,
+					    sizeof(dev->gb), &dev->gb_len);
+		if (dev->gb_len == 0)
 			tm_protocols = 0;
 	}
 
drivers/nfc/pn533/pn533.h +3 -1
@@ -6,6 +6,8 @@
  * Copyright (C) 2012-2013 Tieto Poland
  */
 
+#include <net/nfc/nfc.h>
+
 #define PN533_DEVICE_STD		0x1
 #define PN533_DEVICE_PASORI		0x2
 #define PN533_DEVICE_ACR122U		0x3
@@ -166,7 +168,7 @@ struct pn533 {
 	struct timer_list listen_timer;
 	int cancel_listen;
 
-	u8 *gb;
+	u8 gb[NFC_MAX_GT_LEN];
 	size_t gb_len;
 
 	u8 tgt_available_prots;
drivers/nfc/pn544/pn544.c +3 -4
@@ -377,10 +377,9 @@ static int pn544_hci_start_poll(struct n
 		return r;
 
 	if ((im_protocols | tm_protocols) & NFC_PROTO_NFC_DEP_MASK) {
-		hdev->gb = nfc_get_local_general_bytes(hdev->ndev,
-							&hdev->gb_len);
-		pr_debug("generate local bytes %p\n", hdev->gb);
-		if (hdev->gb == NULL || hdev->gb_len == 0) {
+		nfc_get_local_general_bytes(hdev->ndev, hdev->gb,
+					    sizeof(hdev->gb), &hdev->gb_len);
+		if (hdev->gb_len == 0) {
 			im_protocols &= ~NFC_PROTO_NFC_DEP_MASK;
 			tm_protocols &= ~NFC_PROTO_NFC_DEP_MASK;
 		}
drivers/nfc/st21nfca/core.c +4 -4
@@ -351,10 +351,10 @@ static int st21nfca_hci_start_poll(struc
 			if (r < 0)
 				return r;
 		} else {
-			hdev->gb = nfc_get_local_general_bytes(hdev->ndev,
-							       &hdev->gb_len);
-
-			if (hdev->gb == NULL || hdev->gb_len == 0) {
+			nfc_get_local_general_bytes(hdev->ndev, hdev->gb,
+						    sizeof(hdev->gb),
+						    &hdev->gb_len);
+			if (hdev->gb_len == 0) {
 				im_protocols &= ~NFC_PROTO_NFC_DEP_MASK;
 				tm_protocols &= ~NFC_PROTO_NFC_DEP_MASK;
 			}
include/net/nfc/hci.h +1 -1
@@ -144,7 +144,7 @@ struct nfc_hci_dev {
 	data_exchange_cb_t async_cb;
 	void *async_cb_context;
 
-	u8 *gb;
+	u8 gb[NFC_MAX_GT_LEN];
 	size_t gb_len;
 
 	unsigned long quirks;
include/net/nfc/nfc.h +2 -1
@@ -273,7 +273,8 @@ struct sk_buff *nfc_alloc_recv_skb(unsig
 
 int nfc_set_remote_general_bytes(struct nfc_dev *dev,
 				 const u8 *gt, u8 gt_len);
-u8 *nfc_get_local_general_bytes(struct nfc_dev *dev, size_t *gb_len);
+u8 *nfc_get_local_general_bytes(struct nfc_dev *dev, u8 *out_gb,
+				size_t gb_max_len, size_t *gb_len);
 
 int nfc_fw_download_done(struct nfc_dev *dev, const char *firmware_name,
 			 u32 result);
net/nfc/core.c +7 -8
@@ -279,10 +279,10 @@ static struct nfc_target *nfc_find_targe
 
 int nfc_dep_link_up(struct nfc_dev *dev, int target_index, u8 comm_mode)
 {
-	int rc = 0;
-	u8 *gb;
-	size_t gb_len;
 	struct nfc_target *target;
+	u8 gb[NFC_MAX_GT_LEN];
+	size_t gb_len = 0;
+	int rc = 0;
 
 	pr_debug("dev_name=%s comm %d\n", dev_name(&dev->dev), comm_mode);
 
@@ -301,7 +301,7 @@ int nfc_dep_link_up(struct nfc_dev *dev,
 		goto error;
 	}
 
-	gb = nfc_llcp_general_bytes(dev, &gb_len);
+	nfc_get_local_general_bytes(dev, gb, sizeof(gb), &gb_len);
 	if (gb_len > NFC_MAX_GT_LEN) {
 		rc = -EINVAL;
 		goto error;
@@ -644,11 +644,10 @@ int nfc_set_remote_general_bytes(struct
 }
 EXPORT_SYMBOL(nfc_set_remote_general_bytes);
 
-u8 *nfc_get_local_general_bytes(struct nfc_dev *dev, size_t *gb_len)
+u8 *nfc_get_local_general_bytes(struct nfc_dev *dev, u8 *out_gb,
+				size_t gb_max_len, size_t *gb_len)
 {
-	pr_debug("dev_name=%s\n", dev_name(&dev->dev));
-
-	return nfc_llcp_general_bytes(dev, gb_len);
+	return nfc_llcp_general_bytes(dev, out_gb, gb_max_len, gb_len);
 }
 EXPORT_SYMBOL(nfc_get_local_general_bytes);
 
net/nfc/digital_dep.c +4 -4
@@ -1490,14 +1490,14 @@ static int digital_tg_send_atr_res(struc
 				   struct digital_atr_req *atr_req)
 {
 	struct digital_atr_res *atr_res;
+	u8 gb[NFC_MAX_GT_LEN];
 	struct sk_buff *skb;
-	u8 *gb, payload_bits;
+	u8 payload_bits;
 	size_t gb_len;
 	int rc;
 
-	gb = nfc_get_local_general_bytes(ddev->nfc_dev, &gb_len);
-	if (!gb)
-		gb_len = 0;
+	nfc_get_local_general_bytes(ddev->nfc_dev, gb, sizeof(gb),
+				    &gb_len);
 
 	skb = digital_skb_alloc(ddev, sizeof(struct digital_atr_res) + gb_len);
 	if (!skb)
net/nfc/llcp_core.c +13 -4
@@ -653,23 +653,32 @@ out:
 	return ret;
 }
 
-u8 *nfc_llcp_general_bytes(struct nfc_dev *dev, size_t *general_bytes_len)
+u8 *nfc_llcp_general_bytes(struct nfc_dev *dev, u8 *out_gb, size_t gb_max_len,
+			   size_t *general_bytes_len)
 {
 	struct nfc_llcp_local *local;
 
+	if (!out_gb || !general_bytes_len)
+		return NULL;
+
 	local = nfc_llcp_find_local(dev);
-	if (local == NULL) {
+	if (!local) {
 		*general_bytes_len = 0;
 		return NULL;
 	}
 
 	nfc_llcp_build_gb(local);
 
-	*general_bytes_len = local->gb_len;
+	if (local->gb_len) {
+		*general_bytes_len = min_t(size_t, local->gb_len, gb_max_len);
+		memcpy(out_gb, local->gb, *general_bytes_len);
+	} else {
+		*general_bytes_len = 0;
+	}
 
 	nfc_llcp_local_put(local);
 
-	return local->gb;
+	return out_gb;
 }
 
 int nfc_llcp_set_remote_gb(struct nfc_dev *dev, const u8 *gb, u8 gb_len)
net/nfc/nci/core.c +5 -5
@@ -780,15 +780,15 @@ static int nci_set_local_general_bytes(s
 {
 	struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
 	struct nci_set_config_param param;
+	u8 gb[NFC_MAX_GT_LEN];
 	int rc;
 
-	param.val = nfc_get_local_general_bytes(nfc_dev, &param.len);
-	if ((param.val == NULL) || (param.len == 0))
+	nfc_get_local_general_bytes(nfc_dev, gb, sizeof(gb),
+				    &param.len);
+	if (param.len == 0)
 		return 0;
 
-	if (param.len > NFC_MAX_GT_LEN)
-		return -EINVAL;
-
+	param.val = gb;
 	param.id = NCI_PN_ATR_REQ_GEN_BYTES;
 
 	rc = nci_request(ndev, nci_set_config_req, &param,
net/nfc/nfc.h +2 -1
@@ -49,7 +49,8 @@ void nfc_llcp_mac_is_up(struct nfc_dev *
 int nfc_llcp_register_device(struct nfc_dev *dev);
 void nfc_llcp_unregister_device(struct nfc_dev *dev);
 int nfc_llcp_set_remote_gb(struct nfc_dev *dev, const u8 *gb, u8 gb_len);
-u8 *nfc_llcp_general_bytes(struct nfc_dev *dev, size_t *general_bytes_len);
+u8 *nfc_llcp_general_bytes(struct nfc_dev *dev, u8 *out_gb, size_t gb_max_len,
+			   size_t *general_bytes_len);
 int nfc_llcp_data_received(struct nfc_dev *dev, struct sk_buff *skb);
 struct nfc_llcp_local *nfc_llcp_find_local(struct nfc_dev *dev);
 int nfc_llcp_local_put(struct nfc_llcp_local *local);
author maintainer
#294 lore raw

PATCH 294/395 nfc: llcp: drop truncated I/RR/RNR PDUs in nfc_llcp_recv_hdlc()

In reply to Greg Kroah-Hartman

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

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

From: Aamir Ahmed <[email protected]>

commit 273f9d667cde649f8de9d72b1303cc2f4b658c50 upstream.

nfc_llcp_recv_hdlc() reads the sequence byte skb->data[2], via nfc_llcp_ns()/nfc_llcp_nr(), before any length check. The receive path only guarantees the two-byte LLCP header -- __nfc_llcp_recv() checks it with pskb_may_pull() and nfc_llcp_recv_agf() admits two-byte inner PDUs -- so a two-byte I, RR or RNR PDU reads one byte of uninitialised skb tailroom. The byte becomes N(R)/N(S); a peer can already set those with a well-formed PDU, so this is acting on uninitialised memory, not new peer control.

Guard the read with pskb_may_pull(), as commit 95674f506c63 ("nfc: llcp:
reject PDUs shorter than the LLCP header") did for the two-byte header, so the sequence byte is present and linear before it is read. RR and RNR PDUs are LLCP_HEADER_SIZE + LLCP_SEQUENCE_SIZE bytes and an I PDU is longer, so no valid frame is rejected; a truncated PDU is malformed, so return without a DM reply.

---
 net/nfc/llcp_core.c |    3 +++
 1 file changed, 3 insertions(+)
net/nfc/llcp_core.c +3 -0
@@ -1101,6 +1101,9 @@ static void nfc_llcp_recv_hdlc(struct nf
 	struct sock *sk;
 	u8 dsap, ssap, ptype, ns, nr;
 
+	if (!pskb_may_pull(skb, LLCP_HEADER_SIZE + LLCP_SEQUENCE_SIZE))
+		return;
+
 	ptype = nfc_llcp_ptype(skb);
 	dsap = nfc_llcp_dsap(skb);
 	ssap = nfc_llcp_ssap(skb);
author maintainer
#295 lore raw

PATCH 295/395 nfc: port100: reject frames whose declared length exceeds the received data

In reply to Greg Kroah-Hartman

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

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

From: Doruk Tan Ozturk <[email protected]>

commit 092c6a605cbd6414ef499834c2e0da69c2c3388e upstream.

port100_recv_response() passes the URB transfer buffer to port100_rx_frame_is_valid(), which checksums le16_to_cpu(frame->datalen) bytes of frame->data. datalen is a 16-bit field supplied by the device and is never checked against the number of bytes actually received (urb->actual_length), so a device reporting a datalen larger than the received frame makes port100_data_checksum() read out of bounds past the transfer buffer.

Reject a response whose declared frame size does not fit the received length before validating it.

Found by 0sec (https://0sec.ai) using automated source analysis; the missing bound is evident from source. Compile-tested.

Fixes 562d4d59b8a1 ("NFC: Sony Port-100 Series driver")
Assisted-by 0sec:claude-opus-4-8
---
 drivers/nfc/port100.c |    7 +++++++
 1 file changed, 7 insertions(+)
drivers/nfc/port100.c +7 -0
@@ -636,6 +636,13 @@ static void port100_recv_response(struct
 
 	in_frame = dev->in_urb->transfer_buffer;
 
+	if (urb->actual_length < PORT100_FRAME_HEADER_LEN ||
+	    urb->actual_length < port100_rx_frame_size(in_frame)) {
+		nfc_err(&dev->interface->dev, "Received a truncated frame\n");
+		cmd->status = -EIO;
+		goto sched_wq;
+	}
+
 	if (!port100_rx_frame_is_valid(in_frame)) {
 		nfc_err(&dev->interface->dev, "Received an invalid frame\n");
 		cmd->status = -EIO;
author maintainer
#296 lore raw

PATCH 296/395 nfc: trf7970a: power down on startup RX gain failure

In reply to Greg Kroah-Hartman

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

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

From: Myeonghun Pak <[email protected]>

commit d2acbde7e67df44efa8f0963462d1192e7694ffc upstream.

trf7970a_startup() powers up the device before applying the optional RX gain reduction. If the register read or write fails, it returns without undoing that power-up. Probe's unwind only drops the separate regulator references acquired by probe, leaving the additional VIN enable from startup unbalanced. The system resume caller also has no power-down on this error.

Call trf7970a_power_down() before returning the RX gain error to deassert the enable GPIOs, release the startup VIN reference and restore the powered-off state. Runtime PM has not been enabled yet, so the full shutdown helper is not appropriate here. Preserve the original SPI error.

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

Fixes 5d69351820ea ("NFC: trf7970a: Create device-tree parameter for RX gain reduction")
Assisted-by OpenAI:GPT-5.6
---
 drivers/nfc/trf7970a.c |    4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/nfc/trf7970a.c +3 -1
@@ -1997,8 +1997,10 @@ static int trf7970a_startup(struct trf79
 		return ret;
 
 	ret = trf7970a_update_rx_gain_reduction(trf);
-	if (ret)
+	if (ret) {
+		trf7970a_power_down(trf);
 		return ret;
+	}
 
 	pm_runtime_set_active(trf->dev);
 	pm_runtime_enable(trf->dev);
author maintainer
#297 lore raw

PATCH 297/395 scsi: libiscsi_tcp: Check the data direction of a Data-In PDU

In reply to Greg Kroah-Hartman

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

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

From: Yehyeong Lee <[email protected]>

commit bce07e2f37b5e4a427d36fd6b1c14067b27591db upstream.

The Data-In branch of iscsi_tcp_hdr_dissect() resolves the ITT to a task and copies the PDU's data segment into that command's scatterlist without asking whether the command was reading. iscsi_tcp_r2t_rsp() in the same file does ask, and rejects an R2T for a command that is not DMA_TO_DEVICE.

A target that answers a WRITE command's ITT with a Data-In therefore has the initiator write target-supplied bytes into the pages that write was about to send. Those are the caller's own pinned pages for an O_DIRECT write, and page cache pages for a buffered one.

Observed against a test target that emits one 512-byte Data-In naming a 128 KB write's ITT, after the R2T for that write. With O_DIRECT the caller's buffer ends up holding 512 bytes of the target's data while pwrite() returns 131072. Buffered is quieter: pwrite() and fsync() both succeed, nothing is logged, and reading those blocks back returns the target's bytes out of the page cache without a command going on the wire.

Check the direction before using the scatterlist, the way the R2T path already does.

---
 drivers/scsi/libiscsi_tcp.c |    3 +++
 1 file changed, 3 insertions(+)
drivers/scsi/libiscsi_tcp.c +3 -0
@@ -480,6 +480,9 @@ static int iscsi_tcp_data_in(struct iscs
 	int datasn = be32_to_cpu(rhdr->datasn);
 	unsigned total_in_length = task->sc->sdb.length;
 
+	if (task->sc->sc_data_direction != DMA_FROM_DEVICE)
+		return ISCSI_ERR_PROTO;
+
 	/*
 	 * lib iscsi will update this in the completion handling if there
 	 * is status.
author maintainer
#298 lore raw

PATCH 298/395 parisc: Increase kernel stack size to 32kb

In reply to Greg Kroah-Hartman

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

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

From: Helge Deller <[email protected]>

commit 94b7e3a7e871ae27d4935c76959dfc61829f27f9 upstream.

For 64-bit Linux kernels, increase the default kernel stack size (THREAD_SIZE_ORDER) to 32 kB, in order to avoid kernel crashes which have been triggered recently when building the debian vtk9 package with gcc 17:

stackcheck: kworker/u128:0 will most likely overflow kernel stack (sp:179a83af0, stk bottom-top:179a80000-179a84000)

Kernel panic - not syncing: low stack detected by irq handler - check messages
CPU: 2 UID: 0 PID: 30760 Comm: kworker/u128:0 Tainted: G W 6.18.46-dirty #1 NONE
Tainted: [W]=WARN
Hardware name: 9000/800/rp3440
Workqueue: writeback wb_workfn (flush-259:0)
Backtrace:
 [<000000004022f050>] show_stack+0x70/0x90
 [<000000004022378c>] dump_stack_lvl+0x124/0x190
 [<000000004022382c>] dump_stack+0x34/0x48
 [<000000004020212c>] vpanic+0x204/0x648
 [<00000000402025c4>] panic+0x54/0x58
 [<0000000040232230>] do_cpu_irq_mask+0x3f8/0x440
 [<0000000040227070>] intr_return+0x0/0xc
---
 arch/parisc/include/asm/thread_info.h |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
arch/parisc/include/asm/thread_info.h +1 -1
@@ -24,7 +24,7 @@ struct thread_info {
 
 /* thread information allocation */
 
-#ifdef CONFIG_IRQSTACKS
+#if defined(CONFIG_IRQSTACKS) && !defined(CONFIG_64BIT)
 #define THREAD_SIZE_ORDER	2 /* PA-RISC requires at least 16k stack */
 #else
 #define THREAD_SIZE_ORDER	3 /* PA-RISC requires at least 32k stack */
author maintainer
#299 lore raw

PATCH 299/395 perf/core: Fix NULL pmu_ctx passed to pmu->sched_task()

In reply to Greg Kroah-Hartman

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

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

From: Puranjay Mohan <[email protected]>

commit 36bb85cf36cab15fb611cb44b78a5df06e4e69a2 upstream.

perf_pmu_sched_task() returns early when cpuctx->task_ctx is set, and cpc->task_epc is only non-NULL while a task context is scheduled in on this CPU. __perf_pmu_sched_task() therefore always passes NULL:

  Unable to handle kernel NULL pointer dereference at virtual address 00
  pc : armv8pmu_sched_task+0x14/0x50
  Call trace:
   armv8pmu_sched_task+0x14/0x50 (P)
   perf_pmu_sched_task+0xac/0x108
   __perf_event_task_sched_out+0x6c/0xe0

Pass &cpc->epc instead, the CPU-wide context for this PMU, which the function already dereferences a few lines up to find pmu.

armv8pmu_sched_task() is the only in-tree implementation that dereferences the argument, and it only reads ->pmu, so the oops needs BRBE, added in v6.17.

---
 kernel/events/core.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
kernel/events/core.c +1 -1
@@ -3805,7 +3805,7 @@ static void __perf_pmu_sched_task(struct
 	perf_ctx_lock(cpuctx, cpuctx->task_ctx);
 	perf_pmu_disable(pmu);
 
-	pmu->sched_task(cpc->task_epc, task, sched_in);
+	pmu->sched_task(&cpc->epc, task, sched_in);
 
 	perf_pmu_enable(pmu);
 	perf_ctx_unlock(cpuctx, cpuctx->task_ctx);
author maintainer
#300 lore raw

PATCH 300/395 perf/core: Run sched_task() for PMUs with only CPU-wide events

In reply to Greg Kroah-Hartman

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

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

From: Puranjay Mohan <[email protected]>

commit 3d8d74100954a3b17e5c5e37adfe14e16b1db103 upstream.

perf_pmu_sched_task() returns early when cpuctx->task_ctx is set and leaves the work to perf_ctx_sched_task_cb(), which only walks ctx->pmu_ctx_list. A PMU whose events are all CPU-wide is not on that list, so nothing calls its sched_task(). With

perf record -b -e cycles -a -- ls

armv8pmu_sched_task() is skipped on every switch to a task that has a perf context but no event on that PMU, and BRBE records leak across the task boundary. intel_pmu_lbr_add() calls perf_sched_cb_inc() unconditionally too, so LBR records leak the same way on x86.

Drop the early return and skip only the CPCs that perf_ctx_sched_task_cb() handles. That one needs a gate of its own to make the split exact: it tests cpc->sched_cb_usage, which perf_sched_cb_inc() sets per CPU for every branch stack user, so a task with an event for that PMU pinned to another CPU would be handled twice. On x86 the second __intel_pmu_lbr_restore() finds lbr_stack_state == LBR_NONE and calls intel_pmu_lbr_reset(), throwing away the callstack the first one restored.

cpc->task_epc is set only while a task context is scheduled in, and there is one epc per PMU on ctx->pmu_ctx_list, so the two gates are inverses.

For the CPCs perf_pmu_sched_task() picks up, the callback now runs outside the perf_ctx_disable() and perf_ctx_enable() pair in perf_event_context_sched_in(). __perf_pmu_sched_task() disables the PMU around the call itself.

---
 kernel/events/core.c |   13 +++++++++----
 1 file changed, 9 insertions(+), 4 deletions(-)
kernel/events/core.c +9 -4
@@ -3655,6 +3655,9 @@ static void perf_ctx_sched_task_cb(struc
 	list_for_each_entry(pmu_ctx, &ctx->pmu_ctx_list, pmu_ctx_entry) {
 		cpc = this_cpc(pmu_ctx->pmu);
 
+		if (cpc->task_epc != pmu_ctx)
+			continue;
+
 		if (cpc->sched_cb_usage && pmu_ctx->pmu->sched_task)
 			pmu_ctx->pmu->sched_task(pmu_ctx, task, sched_in);
 	}
@@ -3815,15 +3818,17 @@ static void perf_pmu_sched_task(struct t
 				struct task_struct *next,
 				bool sched_in)
 {
-	struct perf_cpu_context *cpuctx = this_cpu_ptr(&perf_cpu_context);
 	struct perf_cpu_pmu_context *cpc, *cpc2;
 
-	/* cpuctx->task_ctx will be handled in perf_event_context_sched_in/out */
-	if (prev == next || cpuctx->task_ctx)
+	if (prev == next)
 		return;
 
-	list_for_each_entry_safe(cpc, cpc2, this_cpu_ptr(&sched_cb_list), sched_cb_entry)
+	list_for_each_entry_safe(cpc, cpc2, this_cpu_ptr(&sched_cb_list), sched_cb_entry) {
+		if (cpc->task_epc)
+			continue;
+
 		__perf_pmu_sched_task(cpc, sched_in ? next : prev, sched_in);
+	}
 }
 
 static void perf_event_switch(struct task_struct *task,
author maintainer
#301 lore raw

PATCH 301/395 pinctrl: single: free the IRQ on domain creation failure

In reply to Greg Kroah-Hartman

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

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

From: Myeonghun Pak <[email protected]>

commit 1d9bb9c870632adea249cfdec49ac37e6f069164 upstream.

pcs_irq_init_chained_handler() requests a shared IRQ on affected SoCs, but its domain creation failure path only removes a chained handler. That does not release the action installed by request_irq(). The probe can continue without interrupt support while leaving the shared IRQ action registered.

Use pcs_irq_free() to undo the appropriate type of handler registration. At this point pcs->domain is NULL, so the helper only releases the parent IRQ handler. Then mark the IRQ invalid, as the other initialization error paths already do, to prevent another release from a later probe unwind or remove.

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

Fixes 3e6cee1786a1 ("pinctrl: single: Add support for wake-up interrupts")
Assisted-by LLM
---
 drivers/pinctrl/pinctrl-single.c |    3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/pinctrl/pinctrl-single.c +2 -1
@@ -1627,7 +1627,8 @@ static int pcs_irq_init_chained_handler(
 					       &pcs_irqdomain_ops,
 					       pcs_soc);
 	if (!pcs->domain) {
-		irq_set_chained_handler(pcs_soc->irq, NULL);
+		pcs_irq_free(pcs);
+		pcs_soc->irq = -1;
 		return -EINVAL;
 	}
 
author maintainer
#302 lore raw

PATCH 302/395 pinctrl: sunxi: keep a shadow copy of the data register output latches

In reply to Greg Kroah-Hartman

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

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

From: Ilya Titov <[email protected]>

commit a13f7f5d14af9baf34eb12c25962f8d3542b281d upstream.

On Allwinner SoCs, reading a bank's data register returns the pin level, not the output latch, for pins that are muxed as inputs. Writing a GPIO therefore corrupts the output latches of all input-muxed pins in the same bank: the read-modify-write in sunxi_pinctrl_gpio_set() reads back their pin levels and writes those into their latches.

This breaks emulated open-drain lines (e.g. a bit-banged I2C bus from i2c-gpio). Such a line is released high by muxing it as input and letting the pull-up raise it, so any concurrent GPIO write in the same bank stores 1 into its latch. Driving the line low afterwards is a non-atomic data-then-mux sequence in sunxi_pinctrl_gpio_direction_output(); if the poisoning write lands between the two steps, the pin actively drives high (push-pull) instead of low.

Observed in practice as sporadic glitches on a T507 board bit-banging I2C on port E while other PE GPIOs are toggled. On a scope the failure is unmistakable: on a clock pulse where SCL should fall to GND, the line instead steps *above* its idle high level for the whole low phase — the pad drives a strong push-pull 3.3 V high, higher than the level the pull-up sustains on the loaded bus — before the next transition recovers it. The same can hit SDA, corrupting data instead of clocks.

Steps to reproduce on any sunxi board with a bit-banged (i2c-gpio) bus:

  # background: toggle any other GPIO of the same bank, e.g. line 21
  gpioset -c <chip> --toggle 100us 21=0 &

# foreground: keep the bit-banged bus busy
while :; do i2cdetect -y <bus> 0x50 0x57; done

  # watch SCL/SDA with a scope or logic analyzer: sporadic clock-low
  # phases driven high (above the pull-up level) instead of low

The bank spinlock cannot help: the racing write is a perfectly valid whole-register RMW that faithfully writes back what the hardware returned. There are no set/clear registers on this IP to write a single bit atomically.

Fix it the same way gpio-mmio handles hardware whose data register read does not return the output latch: keep a shadow copy of each bank's latches, base the read-modify-write on the shadow, and only write the register. The shadow is seeded from the hardware at probe time so pins left in output mode by the bootloader keep their state. Pins that reach output mode through the gpiolib paths write their value (and thereby their shadow bit) before the mux switch in sunxi_pinctrl_gpio_direction_output(); pins muxed to gpio_out directly through a pinmux node bypass that path, so sunxi_pmx_set() refreshes their shadow bit from the latch (readable once the pin is in output mode) to keep them driving their pre-existing level.

Seeding the shadow reads the PIO registers at probe time, which requires the bus clock to be enabled. The clock was only requested at the very end of probe, after devm_pinctrl_register() had already claimed the pin hogs described in the device tree - which mux pins, and thus access registers, with the clock still gated. Move the request ahead of both. Boards whose bootloader leaves the PIO clock running are unaffected, which is why the pre-existing hog problem has gone unnoticed since commit 950707c0eb5c ("pinctrl: sunxi: add clock support").

Fixes df7b34f4c3d2 ("pinctrl: sunxi: Fix gpio_set behaviour")
---
 drivers/pinctrl/sunxi/pinctrl-sunxi.c |   76 +++++++++++++++++++++++++++-------
 drivers/pinctrl/sunxi/pinctrl-sunxi.h |    7 +++
 2 files changed, 68 insertions(+), 15 deletions(-)
drivers/pinctrl/sunxi/pinctrl-sunxi.c +61 -15
@@ -814,6 +814,21 @@ static void sunxi_pmx_set(struct pinctrl
 	writel((readl(pctl->membase + reg) & ~mask) | config << shift,
 	       pctl->membase + reg);
 
+	/*
+	 * A pin muxed to gpio_out directly through a pinmux node bypasses
+	 * sunxi_pinctrl_gpio_set() and drives whatever its output latch
+	 * holds.  Now that the pin is in output mode the data register
+	 * reads back the latch, so refresh the shadow to keep such pins
+	 * driving their pre-existing level.
+	 */
+	if (config == SUN4I_FUNC_OUTPUT) {
+		u32 *shadow = &pctl->dat_shadow[pin / PINS_PER_BANK];
+
+		sunxi_data_reg(pctl, pin, &reg, &shift, &mask);
+		*shadow = (*shadow & ~mask) |
+			  (readl(pctl->membase + reg) & mask);
+	}
+
 	raw_spin_unlock_irqrestore(&pctl->lock, flags);
 }
 
@@ -970,21 +985,29 @@ static int sunxi_pinctrl_gpio_set(struct
 				  int value)
 {
 	struct sunxi_pinctrl *pctl = gpiochip_get_data(chip);
-	u32 reg, shift, mask, val;
+	u32 *shadow = &pctl->dat_shadow[offset / PINS_PER_BANK];
+	u32 reg, shift, mask;
 	unsigned long flags;
 
 	sunxi_data_reg(pctl, offset, &reg, &shift, &mask);
 
 	raw_spin_lock_irqsave(&pctl->lock, flags);
 
-	val = readl(pctl->membase + reg);
-
+	/*
+	 * Reading the data register returns the pin level, not the output
+	 * latch, for pins muxed as inputs.  A read-modify-write based on
+	 * the register would therefore corrupt the latches of input-muxed
+	 * pins in the same bank (e.g. an emulated open-drain I2C line
+	 * released high), making them drive the wrong level once switched
+	 * to output.  Base the read-modify-write on a shadow copy of the
+	 * latches instead.
+	 */
 	if (value)
-		val |= mask;
+		*shadow |= mask;
 	else
-		val &= ~mask;
+		*shadow &= ~mask;
 
-	writel(val, pctl->membase + reg);
+	writel(*shadow, pctl->membase + reg);
 
 	raw_spin_unlock_irqrestore(&pctl->lock, flags);
 
@@ -1510,7 +1533,7 @@ int sunxi_pinctrl_init_with_flags(struct
 	struct pinctrl_pin_desc *pins;
 	struct sunxi_pinctrl *pctl;
 	struct pinmux_ops *pmxops;
-	int i, ret, last_pin, pin_idx;
+	int i, ret, last_pin, pin_idx, nbanks;
 	struct clk *clk;
 
 	pctl = devm_kzalloc(&pdev->dev, sizeof(*pctl), GFP_KERNEL);
@@ -1548,6 +1571,37 @@ int sunxi_pinctrl_init_with_flags(struct
 	if (!pctl->irq_array)
 		return -ENOMEM;
 
+	/*
+	 * The bus clock has to be enabled before the pinctrl device
+	 * registers, as the pin hogs claimed from there access registers.
+	 */
+	ret = of_clk_get_parent_count(node);
+	clk = devm_clk_get_enabled(&pdev->dev, ret == 1 ? NULL : "apb");
+	if (IS_ERR(clk))
+		return PTR_ERR(clk);
+
+	/*
+	 * Seed the output latch shadow from the hardware so pins the
+	 * bootloader left in output mode keep their state; see
+	 * sunxi_pinctrl_gpio_set() for why a shadow is needed.  This must
+	 * happen before the pinctrl device registers, as pin hogs can mux
+	 * pins to gpio_out and thereby update the shadow.
+	 */
+	last_pin = pctl->desc->pins[pctl->desc->npins - 1].pin.number;
+	nbanks = DIV_ROUND_UP(last_pin + 1 - pctl->desc->pin_base,
+			      PINS_PER_BANK);
+	pctl->dat_shadow = devm_kcalloc(&pdev->dev, nbanks,
+					sizeof(*pctl->dat_shadow), GFP_KERNEL);
+	if (!pctl->dat_shadow)
+		return -ENOMEM;
+
+	for (i = 0; i < nbanks; i++) {
+		u32 reg, shift, mask;
+
+		sunxi_data_reg(pctl, i * PINS_PER_BANK, &reg, &shift, &mask);
+		pctl->dat_shadow[i] = readl(pctl->membase + reg);
+	}
+
 	ret = sunxi_pinctrl_build_state(pdev);
 	if (ret) {
 		dev_err(&pdev->dev, "dt probe failed: %d\n", ret);
@@ -1602,7 +1656,6 @@ int sunxi_pinctrl_init_with_flags(struct
 	if (!pctl->chip)
 		return -ENOMEM;
 
-	last_pin = pctl->desc->pins[pctl->desc->npins - 1].pin.number;
 	pctl->chip->owner = THIS_MODULE;
 	pctl->chip->request = gpiochip_generic_request;
 	pctl->chip->free = gpiochip_generic_free;
@@ -1635,13 +1688,6 @@ int sunxi_pinctrl_init_with_flags(struct
 			goto gpiochip_error;
 	}
 
-	ret = of_clk_get_parent_count(node);
-	clk = devm_clk_get_enabled(&pdev->dev, ret == 1 ? NULL : "apb");
-	if (IS_ERR(clk)) {
-		ret = PTR_ERR(clk);
-		goto gpiochip_error;
-	}
-
 	pctl->irq = devm_kcalloc(&pdev->dev,
 				 pctl->desc->irq_banks,
 				 sizeof(*pctl->irq),
drivers/pinctrl/sunxi/pinctrl-sunxi.h +7 -0
@@ -85,6 +85,7 @@
 #define IO_BIAS_MASK		GENMASK(3, 0)
 
 #define SUN4I_FUNC_INPUT	0
+#define SUN4I_FUNC_OUTPUT	1
 #define SUN4I_FUNC_IRQ		6
 
 #define SUNXI_PINCTRL_VARIANT_MASK	GENMASK(7, 0)
@@ -175,6 +176,12 @@ struct sunxi_pinctrl {
 	int				*irq;
 	unsigned			*irq_array;
 	raw_spinlock_t			lock;
+	/*
+	 * Output latch shadow, one word per bank.  Seeded lockless at
+	 * probe before the pinctrl device registers, protected by @lock
+	 * afterwards.
+	 */
+	u32				*dat_shadow;
 	struct pinctrl_dev		*pctl_dev;
 	unsigned long			variant;
 	u32				bank_mem_size;
author maintainer
#303 lore raw

PATCH 303/395 s390/cio: Fix NULL pointer dereference in ccw_device_get_util_str()

In reply to Greg Kroah-Hartman

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

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

From: Vineeth Vijayan <[email protected]>

commit 5b76268dac968612f7283d59b539036de955b7d9 upstream.

The channel path registry entry associated with a CHPID may be removed while the subchannel's PMCW still references that CHPID. In this case, chpid_to_chp() can return NULL, leading to a NULL pointer dereference.

Add the missing NULL check before dereferencing the returned pointer.

Fixes 199652309a4d ("s390/cio: add helper to query utility strings per given ccw device")
---
 drivers/s390/cio/device_ops.c |    2 ++
 1 file changed, 2 insertions(+)
drivers/s390/cio/device_ops.c +2 -0
@@ -517,6 +517,8 @@ u8 *ccw_device_get_util_str(struct ccw_d
 	chp_id_init(&chpid);
 	chpid.id = sch->schib.pmcw.chpid[chp_idx];
 	chp = chpid_to_chp(chpid);
+	if (!chp)
+		return NULL;
 
 	util_str = kmalloc(sizeof(chp->desc_fmt3.util_str), GFP_KERNEL);
 	if (!util_str)
author maintainer
#304 lore raw

PATCH 304/395 s390/cmf: Fix virtual vs physical address confusion

In reply to Greg Kroah-Hartman

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

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

From: Peter Oberparleiter <[email protected]>

commit f4d04425e66af2ecee9d1a49ae0484436f3c2fd1 upstream.

The measurement block address is an absolute address. Define the associated schib_config and schib fields as dma64_t to enable automatic detection of incorrect assignments. Also add the missing virt_to_dma64() translation.

Without this fix, a wrong address will be used by firmware when storing extended format channel measurement data on kernels built with CONFIG_RANDOMIZE_IDENTITY_BASE=y.

Fixes 14edd0d73bfe ("s390/cmf: fix virtual vs physical address confusion")
---
 drivers/s390/cio/cio.h |    5 +++--
 drivers/s390/cio/cmf.c |    2 +-
 2 files changed, 4 insertions(+), 3 deletions(-)
drivers/s390/cio/cio.h +3 -2
@@ -7,6 +7,7 @@
 #include <linux/mod_devicetable.h>
 #include <asm/chpid.h>
 #include <asm/cio.h>
+#include <asm/dma-types.h>
 #include <asm/fcx.h>
 #include <asm/schid.h>
 #include <asm/tpi.h>
@@ -49,7 +50,7 @@ struct pmcw {
 
 /* Target SCHIB configuration. */
 struct schib_config {
-	u64 mba;
+	dma64_t mba;
 	u32 intparm;
 	u16 mbi;
 	u32 isc:3;
@@ -66,7 +67,7 @@ struct schib_config {
 struct schib {
 	struct pmcw pmcw;	 /* path management control word */
 	union scsw scsw;	 /* subchannel status word */
-	__u64 mba;               /* measurement block address */
+	dma64_t mba;		 /* measurement block address */
 	__u8 mda[4];		 /* model dependent area */
 } __attribute__ ((packed,aligned(4)));
 
drivers/s390/cio/cmf.c +1 -1
@@ -184,7 +184,7 @@ static int set_schib(struct ccw_device *
 	sch->config.mbfc = mbfc;
 	/* address can be either a block address or a block index */
 	if (mbfc)
-		sch->config.mba = address;
+		sch->config.mba = address ? virt_to_dma64((void *)address) : 0;
 	else
 		sch->config.mbi = address;
 
author maintainer
#305 lore raw

PATCH 305/395 s390/pci: Fix leak of struct pci_dev reference in zpci_report_status()

In reply to Greg Kroah-Hartman

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

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

From: Niklas Schnelle <[email protected]>

commit 09b7040a1b79f4f61cdad6d9af972e045bb7c498 upstream.

In zpci_report_status(), a reference to the pdev associated with the zdev being reported about is acquired using pci_get_slot(). This reference needs to be dropped with pci_dev_put(), but this call is missing, thus leaking the reference. On subsequent hot unplug, this will cause the struct pci_dev to not be released, leaking memory and preventing reattach.

At the same time, the only existing caller already holds a pdev reference. So instead of reacquiring and then dropping another reference, simply pass the existing pdev pointer to zpci_report_status(). This gets rid of the need for pci_get_slot() as well as the zdev->zbus check.

Fixes 4ec6054e7321 ("s390/pci: Report PCI error recovery results via SCLP")
---
 arch/s390/pci/pci_event.c  |    2 +-
 arch/s390/pci/pci_report.c |   11 +++++------
 arch/s390/pci/pci_report.h |    4 +++-
 3 files changed, 9 insertions(+), 8 deletions(-)
arch/s390/pci/pci_event.c +1 -1
@@ -258,7 +258,7 @@ static pci_ers_result_t zpci_event_attem
 	pci_uevent_ers(pdev, PCI_ERS_RESULT_RECOVERED);
 out_unlock:
 	device_unlock(&pdev->dev);
-	zpci_report_status(zdev, "recovery", status_str);
+	zpci_report_status(zdev, pdev, "recovery", status_str);
 
 	return ers_res;
 }
arch/s390/pci/pci_report.c +5 -6
@@ -90,7 +90,8 @@ static struct debug_view debug_log_view
 
 /**
  * zpci_report_status - Report the status of operations on a PCI device
- * @zdev:	The PCI device for which to report status
+ * @zdev:	The zPCI device for which to report status
+ * @pdev:	The PCI device associated with the zdev if any, NULL otherwise
  * @operation:	A string representing the operation reported
  * @status:	A string representing the status of the operation
  *
@@ -104,15 +105,15 @@ static struct debug_view debug_log_view
  *
  * Return: 0 on success an error code < 0 otherwise.
  */
-int zpci_report_status(struct zpci_dev *zdev, const char *operation, const char *status)
+int zpci_report_status(struct zpci_dev *zdev, struct pci_dev *pdev,
+		       const char *operation, const char *status)
 {
 	struct zpci_report_error *report;
 	struct pci_driver *driver = NULL;
-	struct pci_dev *pdev = NULL;
 	char *buf, *end;
 	int ret;
 
-	if (!zdev || !zdev->zbus)
+	if (!zdev)
 		return -ENODEV;
 
 	/* Protected virtualization hosts get nothing from us */
@@ -122,8 +123,6 @@ int zpci_report_status(struct zpci_dev *
 	report = (void *)get_zeroed_page(GFP_KERNEL);
 	if (!report)
 		return -ENOMEM;
-	if (zdev->zbus->bus)
-		pdev = pci_get_slot(zdev->zbus->bus, zdev->devfn);
 	if (pdev)
 		driver = to_pci_driver(pdev->dev.driver);
 
arch/s390/pci/pci_report.h +3 -1
@@ -8,9 +8,11 @@
  */
 #ifndef __S390_PCI_REPORT_H
 #define __S390_PCI_REPORT_H
+#include <linux/pci.h>
 
 struct zpci_dev;
 
-int zpci_report_status(struct zpci_dev *zdev, const char *operation, const char *status);
+int zpci_report_status(struct zpci_dev *zdev, struct pci_dev *pdev,
+		       const char *operation, const char *status);
 
 #endif /* __S390_PCI_REPORT_H */
author maintainer
#306 lore raw

PATCH 306/395 s390/pci: Fix missing device lock in zpci_report_status()

In reply to Greg Kroah-Hartman

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

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

From: Niklas Schnelle <[email protected]>

commit 0261aef4b15efcee2860ab857e5cb05e9bfa47b0 upstream.

When pdev is non-NULL, zpci_report_status() accesses the device's driver. To get a consistent state matching the recovery, the device lock needs to be held. Do so by expanding the existing device lock critical section.

The lock only needs to be held when the pdev is non-NULL, so extract the pdev-specific reporting into a helper function which also adds a lockdep assertion to detect calls without the device lock held.

Fixes 4ec6054e7321 ("s390/pci: Report PCI error recovery results via SCLP")
---
 arch/s390/pci/pci_event.c  |    2 +-
 arch/s390/pci/pci_report.c |   21 +++++++++++++++------
 2 files changed, 16 insertions(+), 7 deletions(-)
arch/s390/pci/pci_event.c +1 -1
@@ -257,8 +257,8 @@ static pci_ers_result_t zpci_event_attem
 		driver->err_handler->resume(pdev);
 	pci_uevent_ers(pdev, PCI_ERS_RESULT_RECOVERED);
 out_unlock:
-	device_unlock(&pdev->dev);
 	zpci_report_status(zdev, pdev, "recovery", status_str);
+	device_unlock(&pdev->dev);
 
 	return ers_res;
 }
arch/s390/pci/pci_report.c +15 -6
@@ -88,6 +88,19 @@ static struct debug_view debug_log_view
 	NULL
 };
 
+static ssize_t zpci_report_pdev(struct pci_dev *pdev, char *buf, size_t size)
+{
+	struct pci_driver *driver;
+	const char *start = buf;
+	char *end = buf + size;
+
+	device_lock_assert(&pdev->dev);
+	buf += scnprintf(buf, end - buf, "state: %s\n", zpci_state_str(pdev->error_state));
+	driver = to_pci_driver(pdev->dev.driver);
+	buf += scnprintf(buf, end - buf, "driver: %s\n", (driver) ? driver->name : "n/a");
+	return buf - start;
+}
+
 /**
  * zpci_report_status - Report the status of operations on a PCI device
  * @zdev:	The zPCI device for which to report status
@@ -109,7 +122,6 @@ int zpci_report_status(struct zpci_dev *
 		       const char *operation, const char *status)
 {
 	struct zpci_report_error *report;
-	struct pci_driver *driver = NULL;
 	char *buf, *end;
 	int ret;
 
@@ -123,16 +135,13 @@ int zpci_report_status(struct zpci_dev *
 	report = (void *)get_zeroed_page(GFP_KERNEL);
 	if (!report)
 		return -ENOMEM;
-	if (pdev)
-		driver = to_pci_driver(pdev->dev.driver);
 
 	buf = report->data.log_data;
 	end = report->data.log_data + ZPCI_REPORT_DATA_SIZE;
 	buf += scnprintf(buf, end - buf, "report: %s\n", operation);
 	buf += scnprintf(buf, end - buf, "status: %s\n", status);
-	buf += scnprintf(buf, end - buf, "state: %s\n",
-			 (pdev) ? zpci_state_str(pdev->error_state) : "n/a");
-	buf += scnprintf(buf, end - buf, "driver: %s\n", (driver) ? driver->name : "n/a");
+	if (pdev)
+		buf += zpci_report_pdev(pdev, buf, end - buf);
 	ret = debug_dump(pci_debug_msg_id, &debug_log_view, buf, end - buf, true);
 	if (ret < 0)
 		pr_err("Reading PCI debug messages failed with code %d\n", ret);
author maintainer
#307 lore raw

PATCH 307/395 s390/pci: Report SCLP status on error events when no pdev is associated

In reply to Greg Kroah-Hartman

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

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

From: Niklas Schnelle <[email protected]>

commit a1120bea9bc8ea9d9ab2f9904a63b9a228bf2ffc upstream.

With commit 4ec6054e7321 ("s390/pci: Report PCI error recovery results via SCLP") SCLP reports are generated when recovery is performed in response to an error event. If such an error event arrives but no pdev is currently associated with the zdev, e.g. because it was removed or not yet probed, no report is generated. Fix this by generating a report specific to an error event for a zdev without an associated pdev.

Fixes 4ec6054e7321 ("s390/pci: Report PCI error recovery results via SCLP")
---
 arch/s390/pci/pci_event.c |    4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
arch/s390/pci/pci_event.c +3 -1
@@ -321,8 +321,10 @@ static void __zpci_event_error(struct zp
 	pr_err("%s: Event 0x%x reports an error for PCI function 0x%x\n",
 	       pdev ? pci_name(pdev) : "n/a", ccdf->pec, ccdf->fid);
 
-	if (!pdev)
+	if (!pdev) {
+		zpci_report_status(zdev, NULL, "error event", "no pdev bound");
 		goto no_pdev;
+	}
 
 	switch (ccdf->pec) {
 	case 0x002a: /* Error event concerns FMB */
author maintainer
#308 lore raw

PATCH 308/395 s390/pci: Dont report recovery success on skipped recovery

In reply to Greg Kroah-Hartman

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

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

From: Niklas Schnelle <[email protected]>

commit 3a43be7a1fd06a35cf9e621b88283b3b6e7d281c upstream.

When a PCI device is already in the permanent failure state, recovery is skipped, but the SCLP recovery report still shows success. Fix this by changing the status string to explicitly state that recovery was skipped due to permanent failure.

Fixes 4ec6054e7321 ("s390/pci: Report PCI error recovery results via SCLP")
---
 arch/s390/pci/pci_event.c |    1 +
 1 file changed, 1 insertion(+)
arch/s390/pci/pci_event.c +1 -0
@@ -191,6 +191,7 @@ static pci_ers_result_t zpci_event_attem
 	device_lock(&pdev->dev);
 	if (pdev->error_state == pci_channel_io_perm_failure) {
 		ers_res = PCI_ERS_RESULT_DISCONNECT;
+		status_str = "skipped (permanent failure)";
 		goto out_unlock;
 	}
 	pdev->error_state = pci_channel_io_frozen;
author maintainer
#309 lore raw

PATCH 309/395 s390/vfio-ap: fix KVM GISC and page leak when queue removed from host config

In reply to Greg Kroah-Hartman

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

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

From: Anthony Krowiak <[email protected]>

commit 65e05ec252a9b79e75930d3c4dd42d8877db04c5 upstream.

Three related problems exist in the handling of KVM interrupt and page resources when a queue is removed from the host's AP configuration while assigned to a mediated device (mdev).

Problem 1:
~~~~~~~~~
AP_RESPONSE_Q_NOT_AVAIL not handled in vfio_ap_mdev_reset_queue()

When the AP bus removes a queue device whose adapter or domain has been removed from the host's AP configuration, vfio_ap_mdev_remove_queue() is called. If the queue is still in the host's AP configuration at that point, it calls vfio_ap_mdev_reset_queue(), which issues a PQAP(ZAPQ). Since the adapter is already gone from the host configuration, ap_zapq() returns AP_RESPONSE_Q_NOT_AVAIL (0x01). This response code is not handled in vfio_ap_mdev_reset_queue()'s switch statement and falls through to the default case, which issues a WARN but does not call vfio_ap_free_aqic_resources(). As a result, if IRQ handling was enabled for the queue by the guest, the KVM GISC registration and the pinned guest page holding the notification indicator byte (NIB) are both leaked.

This is fixed by adding AP_RESPONSE_Q_NOT_AVAIL to the same case as AP_RESPONSE_DECONFIGURED and AP_RESPONSE_CHECKSTOPPED in vfio_ap_mdev_reset_queue(). Like those response codes, Q_NOT_AVAIL indicates the queue is not operational and no further reset attempts are possible; the correct action is to free the IRQ resources immediately.

Problem 2:
~~~~~~~~~
AP_RESPONSE_Q_NOT_AVAIL not handled in apq_status_check()

In vfio_ap_mdev_reset_queue(), there are four cases that indicate a queue reset has not yet completed, in which case apq_reset_check() is queued to a work queue to verify completion of the reset operation. This function uses the PQAP(TAPQ) function to get the queue's status and calls apq_status_check() to verify whether the reset has completed, failed or needs to be executed again. As described in Problem #1 above, apq_reset_check() does not specifically check for AP_RESPONSE_Q_NOT_AVAIL, thereby potentially leaking KVM GISC registration and the pinned guest page holding the NIB.

This is fixed by adding a case statement for AP_RESPONSE_Q_NOT_AVAIL to apq_status_check() and returning -ENODEV for that case. The caller, apq_reset_check() will then check for this return code and call vfio_ap_free_aqic_resources() to prevent the leak.

Problem 3:
~~~~~~~~~
vfio_ap_free_aqic_resources() leaks saved_isc when kvm is NULL

vfio_ap_free_aqic_resources() guards the call to
kvm_s390_gisc_unregister() with:

    if (q->saved_isc != VFIO_AP_ISC_INVALID &&
        !WARN_ON(!(q->matrix_mdev && q->matrix_mdev->kvm)))

If matrix_mdev->kvm is NULL -- which can happen when vfio_ap_mdev_unset_kvm() has already run and cleared kvm before a subsequent cleanup path reaches this function -- the WARN_ON fires and the entire block is skipped. This leaves q->saved_isc set to a non-invalid value, creating a potential double-free on any subsequent call to this function.

When kvm is NULL the KVM guest is already torn down, so kvm_s390_gisc_unregister() need not and cannot be called; however, q->saved_isc must always be cleared. Fix this by separating the kvm_s390_gisc_unregister() call from the q->saved_isc reset. The WARN_ON now guards only the genuinely impossible case of matrix_mdev being NULL. A NULL kvm is handled gracefully by skipping only the unregister call, and q->saved_isc = VFIO_AP_ISC_INVALID is set unconditionally whenever saved_isc was not already invalid.

Additionally, add an else clause to the host-config check in vfio_ap_mdev_remove_queue() to call vfio_ap_free_aqic_resources() directly when the queue is not in the host's AP configuration. This serves as a backstop: when the AP bus fires the driver .remove callback after an adapter is removed from the host config, the queue is by definition no longer addressable, so vfio_ap_mdev_reset_queue() would always return Q_NOT_AVAIL. The else clause handles this case directly without the unnecessary ap_zapq() call, and ensures cleanup occurs even if kvm has already been set to NULL by a prior call to vfio_ap_mdev_unset_kvm().

Fixes b9bd10c43456d ("s390/vfio-ap: do not reset queue removed from host config")

Message-ID: <[email protected]>

---
 drivers/s390/crypto/vfio_ap_ops.c |   18 ++++++++++++++----
 1 file changed, 14 insertions(+), 4 deletions(-)
drivers/s390/crypto/vfio_ap_ops.c +14 -4
@@ -277,9 +277,9 @@ static void vfio_ap_free_aqic_resources(
 {
 	if (!q)
 		return;
-	if (q->saved_isc != VFIO_AP_ISC_INVALID &&
-	    !WARN_ON(!(q->matrix_mdev && q->matrix_mdev->kvm))) {
-		kvm_s390_gisc_unregister(q->matrix_mdev->kvm, q->saved_isc);
+	if (q->saved_isc != VFIO_AP_ISC_INVALID) {
+		if (!WARN_ON(!q->matrix_mdev) && q->matrix_mdev->kvm)
+			kvm_s390_gisc_unregister(q->matrix_mdev->kvm, q->saved_isc);
 		q->saved_isc = VFIO_AP_ISC_INVALID;
 	}
 	if (q->saved_iova && !WARN_ON(!q->matrix_mdev)) {
@@ -1935,6 +1935,8 @@ static int apq_status_check(int apqn, st
 		 * a value indicating a reset needs to be performed again.
 		 */
 		return -EAGAIN;
+	case AP_RESPONSE_Q_NOT_AVAIL:
+		return -ENODEV;
 	default:
 		WARN(true,
 		     "failed to verify reset of queue %02x.%04x: TAPQ rc=%u\n",
@@ -1961,6 +1963,10 @@ static void apq_reset_check(struct work_
 		ret = apq_status_check(q->apqn, &status);
 		if (ret == -EIO)
 			return;
+		if (ret == -ENODEV) {
+			vfio_ap_free_aqic_resources(q);
+			return;
+		}
 		if (ret == -EBUSY) {
 			pr_notice_ratelimited(WAIT_MSG, elapsed,
 					      AP_QID_CARD(q->apqn),
@@ -2004,6 +2010,7 @@ static void vfio_ap_mdev_reset_queue(str
 		break;
 	case AP_RESPONSE_DECONFIGURED:
 	case AP_RESPONSE_CHECKSTOPPED:
+	case AP_RESPONSE_Q_NOT_AVAIL:
 		vfio_ap_free_aqic_resources(q);
 		break;
 	default:
@@ -2528,12 +2535,15 @@ void vfio_ap_mdev_remove_queue(struct ap
 	/*
 	 * If the queue is not in the host's AP configuration, then resetting
 	 * it will fail with response code 01, (APQN not valid); so, let's make
-	 * sure it is in the host's config.
+	 * sure it is in the host's config. If it is not, free the KVM GISC
+	 * resources.
 	 */
 	if (test_bit_inv(apid, (unsigned long *)matrix_dev->info.apm) &&
 	    test_bit_inv(apqi, (unsigned long *)matrix_dev->info.aqm)) {
 		vfio_ap_mdev_reset_queue(q);
 		flush_work(&q->reset_work);
+	} else {
+		vfio_ap_free_aqic_resources(q);
 	}
 
 done:
author maintainer
#310 lore raw

PATCH 310/395 KVM: Ensure memory attributes xarray nodes are accounted to the callers memcg

In reply to Greg Kroah-Hartman

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

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

From: David Ballesteros <[email protected]>

commit 382e5d514b6f35bdda2ab9044b4eed23d2ec4254 upstream.

Explicitly instantiate the memory attributes xarray with XA_FLAGS_ACCOUNT to ensure that all allocations are accounted to the memcg. Frustratingly, memory allocations done in the "fastpath" do not honor the passed in gfp, even for an explicit xa_reserve(). Only the rare, slow path __xas_nomem() honors the original gfp. E.g.

xa_reserve(..., GFP_KERNEL_ACCOUNT)
|
-> ...

     |
     -> __xa_cmpxchg_raw()
        |
        -> xas_store()  <== does not take @gfp
           |
           -> xas_create()
              |
              -> xas_alloc()

The bug was confirmed by observing that a process in a cgroup limited to 256 MiB grew radix_tree_node slab by ~512 MiB while its memory.current stayed near 0.

Fixes 5a475554db1e ("KVM: Introduce per-page memory attributes")
Assisted-by Claude-Code:claude-opus-5
[sean: rewrite changelog, tag for stable]
---
 virt/kvm/kvm_main.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
virt/kvm/kvm_main.c +1 -1
@@ -1132,7 +1132,7 @@ static struct kvm *kvm_create_vm(unsigne
 	rcuwait_init(&kvm->mn_memslots_update_rcuwait);
 	xa_init(&kvm->vcpu_array);
 #ifdef CONFIG_KVM_GENERIC_MEMORY_ATTRIBUTES
-	xa_init(&kvm->mem_attr_array);
+	xa_init_flags(&kvm->mem_attr_array, XA_FLAGS_ACCOUNT);
 #endif
 
 	INIT_LIST_HEAD(&kvm->gpc_list);
author maintainer
#311 lore raw

PATCH 311/395 KVM: Dont treat reserved xarray entries as having memory attributes

In reply to Greg Kroah-Hartman

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

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

From: Zeng Chi <[email protected]>

commit 277d3623d99a4fc2623bfb7d191b649ca380605d upstream.

kvm_vm_set_mem_attributes() reserves an xarray entry for every gfn in the range before storing the new attributes, so that the store loop can't fail partway through. If one of the reservations fails, e.g. with -ENOMEM, the entries that were already reserved are left in the array. That is harmless as far as xa_reserve() is concerned, as the reserved entries read back as NULL via xa_load(), but it confuses the "does this range have no attributes at all" check:

	if (!attrs)
		return !xas_find(&xas, end - 1);

A reserved entry is XA_ZERO_ENTRY, not NULL, and xas_find() returns it as present. So a leftover reservation makes KVM report that a fully shared range has attributes even though kvm_get_memory_attributes() returns none for every gfn in the range. On x86, the next time mixed-attribute tracking is recomputed for the range (memslot creation, or a later attribute change that straddles the 2MiB page), hugepage_has_attrs() treats a fully shared 2MiB range as mixed and refuses to map it with a hugepage, until userspace happens to set attributes on the range again.

Drop the shortcut and handle the !attrs case in the per-index loop, using xas_next_entry() to find the next non-NULL entry. xas_next_entry() is essentially an optimized xas_find(), so the effective change is that the !attrs lookup now goes through xas_retry() like the attrs != 0 case, i.e. reserved entries are skipped and retry entries restart the walk. Don't check the index when no entry is found, as the xarray leaves the xas index in a bogus state in that case; no entry simply means the rest of the range has no attributes.

KVM never stores a non-NULL entry with a value of zero (clearing stores NULL), but such an entry would be returned by xas_next_entry() and trip the index check, so WARN if one is ever seen.

Fixes 5a475554db1e ("KVM: Introduce per-page memory attributes")
Cc David Ballesteros <[email protected]>
[sean: expand comment to elaborate on xarray APIs, split optimization out]
---
 virt/kvm/kvm_main.c |   28 +++++++++++++++++++++++++---
 1 file changed, 25 insertions(+), 3 deletions(-)
virt/kvm/kvm_main.c +25 -3
@@ -2453,14 +2453,36 @@ bool kvm_range_has_memory_attributes(str
 		return (kvm_get_memory_attributes(kvm, start) & mask) == attrs;
 
 	guard(rcu)();
-	if (!attrs)
-		return !xas_find(&xas, end - 1);
 
+	/*
+	 * Lookup the entry for each index instead of iterating over the xarray
+	 * as KVM deletes/nullifies entries to represent "no attributes", and
+	 * the xas index is effectively invalid when no entry is found.  I.e.
+	 * matching non-zero attributes for *every* entry effectively requires
+	 * a manually lookup for each index.
+	 *
+	 * Skip pre-allocated, reserved entries, or restart the lookup if the
+	 * xarray was concurrently modified, via xas_retry() ("retry" means the
+	 * entry holds an internal xarray value, i.e. is either invalid or NULL
+	 * from the caller's perspective).
+	 *
+	 * Use xas_next() when looking for non-zero attributes to optimize for
+	 * the case where the start of the range (or the entire range) doesn't
+	 * have any attributes, as xas_next() returns literally the next entry,
+	 * whereas xas_next_entry() returns the next non-NULL entry (bounded by
+	 * a maximum index).
+	 */
 	for (index = start; index < end; index++) {
 		do {
-			entry = xas_next(&xas);
+			entry = attrs ? xas_next(&xas) :
+					xas_next_entry(&xas, end - 1);
 		} while (xas_retry(&xas, entry));
 
+		if (!entry)
+			return !attrs;
+
+		WARN_ON_ONCE(!xa_to_value(entry));
+
 		if (xas.xa_index != index ||
 		    (xa_to_value(entry) & mask) != attrs)
 			return false;
author maintainer
#312 lore raw

PATCH 312/395 KVM: SEV: Free have_run_cpus during VM destruction even if VM is no longer SEV

In reply to Greg Kroah-Hartman

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

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

From: Sean Christopherson <[email protected]>

commit 12c1f6e03f944e399bd2c88441dca5dc702b95a5 upstream.

Unconditionally free SEV's "have run CPUs" cpumask in the VM destroy path, i.e. even for what appear to be non-SEV VMs, as an SEV VM becomes a non-SEV VM if its state is intra-host migrated. Alternatively, the mask could be freed in sev_migrate_from() when "converting" the source VM, but that gets annoying because ideally KVM would nullify the mask to guard against UAF, and nullifying the mask would need be conditioned on CPUMASK_OFFSTACK=y.

Freeing the mask during sev_migrate_from() is also not robust against other KVM bugs, though that's kind of a moot point since any such bugs would show up even if sev->active is never set. I.e. KVM must get that side of things correct. But, that's not a great reason to add more code just to make things marginally less robust.

Fixes 6f38f8c57464 ("KVM: SVM: Flush cache only on CPUs running SEV guest")

Message-ID: <[email protected]>

---
 arch/x86/kvm/svm/sev.c |    8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)
arch/x86/kvm/svm/sev.c +6 -2
@@ -2920,13 +2920,17 @@ void sev_vm_destroy(struct kvm *kvm)
 	struct list_head *head = &sev->regions_list;
 	struct list_head *pos, *q;
 
+	/*
+	 * Free the mask even if the VM is not *currently* an SEV VM, as it may
+	 * have been an SEV VM prior to intra-host migration.
+	 */
+	free_cpumask_var(sev->have_run_cpus);
+
 	if (!sev_guest(kvm))
 		return;
 
 	WARN_ON(!list_empty(&sev->mirror_vms));
 
-	free_cpumask_var(sev->have_run_cpus);
-
 	/*
 	 * If this is a mirror VM, remove it from the owner's list of a mirrors
 	 * and skip ASID cleanup (the ASID is tied to the lifetime of the owner).
author maintainer
#313 lore raw

PATCH 313/395 KVM: SEV: Do cache maintenance on the source VM during intra-host migration

In reply to Greg Kroah-Hartman

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

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

From: Sean Christopherson <[email protected]>

commit 93de2a6a4b91b72607136dd656edf03fb399d27f upstream.

Manually perform cache maintenance on the source VM during intra-host migration to ensure no stale data is left in CPU caches after the VM is destroyed. Because the source VM is "converted" to a non-SEV VM, KVM's memory reclaim flows won't trigger cache maintenance, e.g. when all guest memory is reclaimed in response to detaching from the mmu_notifier.

Note, relying on the destination VM to do cache maintenance isn't an option as KVM doesn't require identical guest memory configurations, i.e. the source VM may have access to memory that the destination VM does not. Enforcing equivalent memory configurations is infeasible, as it would require a *deep* comparison of memslots, e.g. to verify that not only are the memslot identical, but what the memslots point at is also identical.

Fixes b56639318bb2 ("KVM: SEV: Add support for SEV intra host migration")

Message-ID: <[email protected]>

---
 arch/x86/kvm/svm/sev.c |   10 ++++++++++
 1 file changed, 10 insertions(+)
arch/x86/kvm/svm/sev.c +10 -0
@@ -1986,6 +1986,12 @@ static void sev_migrate_from(struct kvm
 	src->pages_locked = 0;
 	src->es_active = false;
 
+	/*
+	 * Do cache maintenance on the source VM as it is no longer an SEV VM,
+	 * i.e. memory reclaim flows won't trigger cache maintenance on the VM.
+	 */
+	sev_writeback_caches(src_kvm);
+
 	list_cut_before(&dst->regions_list, &src->regions_list, &src->regions_list);
 
 	mutex_lock(&sev_mirror_lock);
@@ -2125,6 +2131,10 @@ int sev_vm_move_enc_context_from(struct
 	 * the set of CPUs from the source.  If a CPU was used to run a vCPU in
 	 * the source VM but is never used for the destination VM, then the CPU
 	 * can only have cached memory that was accessible to the source VM.
+	 * Furthermore, KVM *must* perform cache maintenance on the source VM,
+	 * as the source VM may have access to memory that the destination VM
+	 * does not, i.e. KVM could skip flushes if memory is reclaimed from
+	 * the old VM but not the new VM.
 	 */
 	if (!zalloc_cpumask_var(&dst_sev->have_run_cpus, GFP_KERNEL_ACCOUNT)) {
 		ret = -ENOMEM;
author maintainer
#314 lore raw

PATCH 314/395 KVM: arm64: Fix AArch32 DBGBXVR<n> handling

In reply to Greg Kroah-Hartman

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

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

From: Karl Mehltretter <[email protected]>

commit 6b1bca1b1ab77f60a62087337bfe6e2f0efb9e6d upstream.

The consolidation of the breakpoint and watchpoint register accessors switched DBGBXVR<n> from trap_bvr() to trap_dbg_wb_reg(). The latter selects backing storage with demux_wb_reg(), which only handles Op2 values 4 through 7. Since DBGBXVR<n> uses Op2 1, an AArch32 guest access hits KVM_BUG_ON() and marks the VM dead.

DBGBXVR<n> aliases DBGBVR<n>_EL1[63:32], and its AA32(HI) descriptor already selects the upper half. Map Op2 1 to dbg_bvr[] alongside Op2 4, restoring the pre-regression behavior.

Fixes 3ce9f3357e9e ("KVM: arm64: Fold DBGxVR/DBGxCR accessors into common set")
Assisted-by Claude:claude-opus-5
---
 arch/arm64/kvm/sys_regs.c |    1 +
 1 file changed, 1 insertion(+)
arch/arm64/kvm/sys_regs.c +1 -0
@@ -792,6 +792,7 @@ static u64 *demux_wb_reg(struct kvm_vcpu
 	struct kvm_guest_debug_arch *dbg = &vcpu->arch.vcpu_debug_state;
 
 	switch (rd->Op2) {
+	case 0b001:
 	case 0b100:
 		return &dbg->dbg_bvr[rd->CRm];
 	case 0b101:
author maintainer
#315 lore raw

PATCH 315/395 KVM: arm64: Fix spurious warning for benign stage 2 teardown race

In reply to Greg Kroah-Hartman

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

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

From: Lorenzo Stoakes (ARM) <[email protected]>

commit 38b70fc453c3112f1a62583b89903ae41116cc27 upstream.

kvmtool was used to establish an L1 guest with 8 CPUs and 8 GiB of RAM, an L2 guest with 4 CPUs and 4 GiB of RAM and an L3 guest with 2 CPUs and 2 GiB of RAM, all of which was then exited.

Under memory pressure in the L0 host warnings were observed due to migration triggered by compaction:

WARNING: arch/arm64/kvm/mmu.c:336 at __unmap_stage2_range+0x64/0x80,

CPU#5: kcompactd0/66

Which was, in turn, triggered by an MMU notifier for the host invalidation:

mmu_notifier_invalidate_range_start()
-> ... -> kvm_mmu_notifier_invalidate_range_start()

    -> kvm_mmu_unmap_gfn_range()
      -> kvm_unmap_gfn_range()
        -> kvm_nested_s2_unmap()
          -> kvm_stage2_unmap_range()
            -> __unmap_stage2_range()
               -> stage2_apply_range()
       	       <- -EINVAL, triggering a WARN_ON()

Racing with L0's teardown of stage 2 page tables:

exit_mm()
-> mmput()

    -> __mmput()
      -> exit_mmap()
        -> mmu_notifier_release()
	  -> ... -> kvm_mmu_notifier_release()
            -> kvm_flush_shadow_all()
              -> kvm_arch_flush_shadow_all()
	        -> kvm_free_stage2_pgd()
		  -> [ acquire kvm->mmu_lock for write ]
		  -> mmu->pgt = NULL [ among other tasks ]
		  -> [ release kvm->mmu_lock for write ]

It turns out there is a benign race resulting in a spurious warning:

	Thread A - notify: migration   | Thread B - notify: release
	-------------------------------|---------------------------------
	< kvm->mmu_lock held >         |
	stage2_apply_range()           |
	  get mmu->pgt, check !NULL    |
	  ...                          | kvm_arch_flush_shadow_all()
	  cond_resched_rwlock_write(); |   < contend, sleep kvm->mmu_lock >
	< drop kvm->mmu_lock >         |   < acquire kvm->mmu_lock>
                                       |   ...
				       |   kvm_free_stage2_pgd()
                                       |     mmu->pgt = NULL
				       |   < invalidate MMU >
				       |   ...
				       |   < release kvm->mmu_lock >
	[ scheduled ]		       |
	stage2_apply_range()           |
	  < loop to next >             |
	  get, mmu->pgt, check !NULL   |
	  is NULL, return -EINVAL      |
        __unmap_stage2_range()         |
	  WARN_ON(-EINVAL) <--- entirely spurious - the race was handled
                                 correctly.

Fix the spurious warning by updating stage2_apply_range() to no longer treat concurrent PGT teardown on lock release as an error - whether the walker is tearing down page tables or doing something else this is a legitimate reason to abort the operation without error.

This keeps the warning in place for all other circumstances.

In practice only __unmap_stage2_range() actually does anything with the error so this only impacts that.

---
 arch/arm64/kvm/mmu.c |   15 ++++++++++++---
 1 file changed, 12 insertions(+), 3 deletions(-)
arch/arm64/kvm/mmu.c +12 -3
@@ -59,27 +59,36 @@ static phys_addr_t stage2_range_addr_end
  * long will also starve other vCPUs. We have to also make sure that the page
  * tables are not freed while we released the lock.
  */
-static int stage2_apply_range(struct kvm_s2_mmu *mmu, phys_addr_t addr,
+static int stage2_apply_range(struct kvm_s2_mmu *mmu, phys_addr_t start,
 			      phys_addr_t end,
 			      int (*fn)(struct kvm_pgtable *, u64, u64),
 			      bool resched)
 {
 	struct kvm *kvm = kvm_s2_mmu_to_kvm(mmu);
+	bool lock_dropped = false;
+	phys_addr_t addr = start;
 	int ret;
 	u64 next;
 
 	do {
 		struct kvm_pgtable *pgt = mmu->pgt;
+		/*
+		 * We may be raced on PGT teardown when we release the
+		 * kvm->mmu_lock. That's fine as the PGT is legitimately no
+		 * longer present.
+		 */
 		if (!pgt)
-			return -EINVAL;
+			return lock_dropped ? 0 : -EINVAL;
 
 		next = stage2_range_addr_end(addr, end);
 		ret = fn(pgt, addr, next - addr);
 		if (ret)
 			break;
 
-		if (resched && next != end)
+		if (resched && next != end) {
 			cond_resched_rwlock_write(&kvm->mmu_lock);
+			lock_dropped = true;
+		}
 	} while (addr = next, addr != end);
 
 	return ret;
author maintainer
#316 lore raw

PATCH 316/395 KVM: arm64: nv: Fix null ptr deref on nested wp/unmap, teardown race

In reply to Greg Kroah-Hartman

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

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

From: Lorenzo Stoakes (ARM) <[email protected]>

commit 4c74e233cdedd11592775fae2a6243e67ca3f891 upstream.

Commit 7270cc9157f4 ("KVM: arm64: nv: Handle VNCR_EL2 invalidation from MMU notifiers") introduced VNCR_EL2 invalidation in both kvm_nested_s2_unmap() and kvm_nested_s2_wp().

However at the point of this being performed concurrent stage 2 teardown of a nested guest can cause kvm->arch.mmu.pgt to be set to NULL.

This happens in kvm_flush_shadow_all() -> kvm_arch_flush_shadow_all() -> kvm_free_stage2_pgd() and is performed under the kvm->mmu_lock.

Commit ec14c272408a ("KVM: arm64: nv: Unmap/flush shadow stage 2 page tables") introduced the teardown of the entire nested MMU range, which then invokes stage2_apply_range() with resched=true:

mmu_notifier_invalidate_range_start()
-> ... -> kvm_mmu_notifier_invalidate_range_start()

    -> kvm_mmu_unmap_gfn_range()
      -> kvm_unmap_gfn_range()
        -> kvm_nested_s2_unmap()
          -> kvm_stage2_unmap_range()
            -> __unmap_stage2_range()
                -> stage2_apply_range()

This means that stage2_apply_range() can drop the kvm->mmu_lock and thus concurrent progress can be made in lockstep with kvm_arch_flush_shadow_all().

If kvm_arch_flush_shadow_all() advances ahead of stage2_apply_range() and completes its operation it guarantees a NULL pointer deref.

Since kvm_free_stage2_pgd() is performed under the kvm->mmu_lock this will either be observed NULL or not and serialised against kvm_free_stage2_pgd().

Resolve the issue by abstracting the invalidation to a new function, kvm_invalidate_vncr_ipa_all(), and check that the pgt is non-NULL before dereferencing it.

---
 arch/arm64/kvm/nested.c |   15 +++++++++++++--
 1 file changed, 13 insertions(+), 2 deletions(-)
arch/arm64/kvm/nested.c +13 -2
@@ -1129,6 +1129,17 @@ void kvm_handle_s1e2_tlbi(struct kvm_vcp
 	invalidate_vncr_va(vcpu->kvm, &scope);
 }
 
+static void kvm_invalidate_vncr_ipa_all(struct kvm *kvm)
+{
+	struct kvm_pgtable *pgt = kvm->arch.mmu.pgt;
+
+	lockdep_assert_held_write(&kvm->mmu_lock);
+
+	/* if the mmu lock was dropped, pgt teardown may have raced. */
+	if (pgt)
+		kvm_invalidate_vncr_ipa(kvm, 0, BIT(pgt->ia_bits));
+}
+
 void kvm_nested_s2_wp(struct kvm *kvm)
 {
 	int i;
@@ -1142,7 +1153,7 @@ void kvm_nested_s2_wp(struct kvm *kvm)
 			kvm_stage2_wp_range(mmu, 0, kvm_phys_size(mmu));
 	}
 
-	kvm_invalidate_vncr_ipa(kvm, 0, BIT(kvm->arch.mmu.pgt->ia_bits));
+	kvm_invalidate_vncr_ipa_all(kvm);
 }
 
 void kvm_nested_s2_unmap(struct kvm *kvm, bool may_block)
@@ -1158,7 +1169,7 @@ void kvm_nested_s2_unmap(struct kvm *kvm
 			kvm_stage2_unmap_range(mmu, 0, kvm_phys_size(mmu), may_block);
 	}
 
-	kvm_invalidate_vncr_ipa(kvm, 0, BIT(kvm->arch.mmu.pgt->ia_bits));
+	kvm_invalidate_vncr_ipa_all(kvm);
 }
 
 void kvm_nested_s2_flush(struct kvm *kvm)
author maintainer
#317 lore raw

PATCH 317/395 KVM: arm64: Transfer the hyp stack pages out of the host stage-2

In reply to Greg Kroah-Hartman

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

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

From: Fuad Tabba <[email protected]>

commit 3a8c562892b96f35bba1e00d5e455a15963bbb92 upstream.

fix_host_ownership() walks only the linear-map alias of each memblock region, and the per-CPU hyp stack, mapped in the private VA range for its guard page, has none.

Walk each stack's VA range with the same walker.

---
 arch/arm64/kvm/hyp/nvhe/setup.c |   10 ++++++++++
 1 file changed, 10 insertions(+)
arch/arm64/kvm/hyp/nvhe/setup.c +10 -0
@@ -269,6 +269,16 @@ static int fix_host_ownership(void)
 			return ret;
 	}
 
+	/* The stacks sit in the private VA range, not the linear map. */
+	for (i = 0; i < hyp_nr_cpus; i++) {
+		struct kvm_nvhe_init_params *params = per_cpu_ptr(&kvm_init_params, i);
+		u64 start = params->stack_hyp_va - NVHE_STACK_SIZE;
+
+		ret = kvm_pgtable_walk(&pkvm_pgtable, start, NVHE_STACK_SIZE, &walker);
+		if (ret)
+			return ret;
+	}
+
 	return 0;
 }
 
author maintainer
#318 lore raw

PATCH 318/395 RISC-V: KVM: Synchronize hrtimer callback during teardown

In reply to Greg Kroah-Hartman

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

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

From: Myeonghun Pak <[email protected]>

commit aaad136d56d91252517272b68cd533e5714698d5 upstream.

The non-Sstc hrtimer callback clears next_set before its final uses of the enclosing vCPU. If teardown observes next_set as false while the callback is still running, kvm_riscv_vcpu_timer_cancel() skips hrtimer_cancel() and kvm_destroy_vcpus() can free the vCPU before the callback enters kvm_riscv_vcpu_set_interrupt().

A guest can arm the timer with SBI TIME and request shutdown with SBI legacy shutdown or SRST. A VMM that honors KVM_EXIT_SYSTEM_EVENT and destroys the VM supplies the teardown side of the race; no post-launch host ioctl is needed to arm or request teardown.

On upstream master 62cc90241548, generic KASAN reported:

  BUG: KASAN: slab-use-after-free in do_raw_spin_lock
  Write of size 4 at addr ff60000005e58898

kvm_riscv_vcpu_set_interrupt
kvm_riscv_vcpu_hrtimer_expired
__hrtimer_run_queues
hrtimer_interrupt

The object was allocated by KVM_CREATE_VCPU and freed concurrently by:

kvm_destroy_vcpus
kvm_arch_destroy_vm
kvm_destroy_vm
__fput

For deterministic validation, I added mdelay(1000) immediately after the existing next_set = false assignment. This only widens the existing post-clear callback window. A no-delay trace build naturally reached the callback-after-teardown-start/before-deinit ordering in 12 of 200 runs, but 1,500 stock-kernel stress iterations did not produce a KASAN report, so natural reproduction is timing-sensitive.

Always invoke hrtimer_cancel() for an initialized timer. Preserve the existing -EINVAL result when the timer is no longer set, but only after synchronizing with a running callback.

With this patch, hrtimer_cancel() blocked for the full widened callback window before vCPU destruction. KASAN reported no error in 100 fixed-and-widened runs or 200 fix-only timing-sweep runs.

Fixes 3a9f66cb25e1 ("RISC-V: KVM: Add timer functionality")
Assisted-by OpenAI:GPT-5.6
---
 arch/riscv/kvm/vcpu_timer.c |    5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)
arch/riscv/kvm/vcpu_timer.c +4 -1
@@ -60,10 +60,13 @@ static enum hrtimer_restart kvm_riscv_vc
 
 static int kvm_riscv_vcpu_timer_cancel(struct kvm_vcpu_timer *t)
 {
-	if (!t->init_done || !t->next_set)
+	if (!t->init_done)
 		return -EINVAL;
 
 	hrtimer_cancel(&t->hrt);
+
+	if (!t->next_set)
+		return -EINVAL;
 	t->next_set = false;
 
 	return 0;
author maintainer
#319 lore raw

PATCH 319/395 RISC-V: KVM: Propagate interrupted G-stage faults

In reply to Greg Kroah-Hartman

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

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

From: Xie Bo <[email protected]>

commit f41fb17143df890855d3de980f8eb92dfd595817 upstream.

__kvm_faultin_pfn() reports an interrupted host page fault with KVM_PFN_ERR_SIGPENDING. RISC-V currently handles it as a generic error PFN and returns -EFAULT.

Return -EINTR for the signal-pending sentinel so callers can distinguish an interrupted fault from an invalid userspace mapping. Do not log the expected interruption as a vCPU exit error.

Fixes 9d05c1fee837 ("RISC-V: KVM: Implement stage2 page table programming")
---
 arch/riscv/kvm/mmu.c       |    2 ++
 arch/riscv/kvm/vcpu_exit.c |    2 +-
 2 files changed, 3 insertions(+), 1 deletion(-)
arch/riscv/kvm/mmu.c +2 -0
@@ -380,6 +380,8 @@ int kvm_riscv_mmu_map(struct kvm_vcpu *v
 				vma_pageshift, current);
 		return 0;
 	}
+	if (is_sigpending_pfn(hfn))
+		return -EINTR;
 	if (is_error_noslot_pfn(hfn))
 		return -EFAULT;
 
arch/riscv/kvm/vcpu_exit.c +1 -1
@@ -248,7 +248,7 @@ int kvm_riscv_vcpu_exit(struct kvm_vcpu
 	}
 
 	/* Print details in-case of error */
-	if (ret < 0) {
+	if (ret < 0 && ret != -EINTR) {
 		kvm_err("VCPU exit error %d\n", ret);
 		kvm_err("SEPC=0x%lx SSTATUS=0x%lx HSTATUS=0x%lx\n",
 			vcpu->arch.guest_context.sepc,
author maintainer
#320 lore raw

PATCH 320/395 RISC-V: KVM: Fix HSM hart status error propagation

In reply to Greg Kroah-Hartman

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

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

From: Tan Chi <[email protected]>

commit 41e81f7e3ef96594fb840445343c0ee7723aa550 upstream.

kvm_sbi_hsm_vcpu_get_status() returns SBI_ERR_INVALID_PARAM when the requested hart does not exist. However, the HART_STATUS case returns from the SBI handler without storing this error in retdata->err_val.

As a result, a guest querying the status of a non-existent hart observes SBI_SUCCESS instead of SBI_ERR_INVALID_PARAM.

Use the common SBI error handling path for HART_STATUS after saving a valid hart state in retdata->out_val. This preserves the returned error when kvm_sbi_hsm_vcpu_get_status() fails.

Fixes bae0dfd74e01 ("RISC-V: KVM: Modify SBI extension handler to return SBI error code")
Assisted-by LLM
---
 arch/riscv/kvm/vcpu_sbi_hsm.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
arch/riscv/kvm/vcpu_sbi_hsm.c +2 -2
@@ -95,9 +95,9 @@ static int kvm_sbi_ext_hsm_handler(struc
 		ret = kvm_sbi_hsm_vcpu_get_status(vcpu);
 		if (ret >= 0) {
 			retdata->out_val = ret;
-			retdata->err_val = 0;
+			ret = 0;
 		}
-		return 0;
+		break;
 	case SBI_EXT_HSM_HART_SUSPEND:
 		switch (lower_32_bits(cp->a0)) {
 		case SBI_HSM_SUSPEND_RET_DEFAULT:
author maintainer
#321 lore raw

PATCH 321/395 Bluetooth: hci_conn: fix CIS hold ownership on reuse

In reply to Greg Kroah-Hartman

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

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

From: Aldo Ariel Panzardo <[email protected]>

commit e06d549fcd4a0ba381ed67ddf1ab3c7a6ca4314c upstream.

Commit 69997d50ec57 ("Bluetooth: ISO: handle bound CIS cleanup via hci_conn") made hci_bind_cis() and hci_connect_cis() return a connection with one hold for the ISO layer. hci_bind_cis() currently takes that hold only after configuring a CIS, so its BT_CONNECTED and matching BT_BOUND paths return a bare lookup result. Its configuration failure path can likewise call hci_conn_drop() before taking a hold.

Take the hold before any state-dependent return or configuration error so every successful return follows the documented ownership contract and every error drop is balanced.

hci_connect_cis() also assumes hci_conn_link() always takes a new CIS hold before dropping the one returned by hci_bind_cis(). However, the helper returns an existing link without taking another hold. In that case, preserve the CIS hold for the caller and drop the redundant LE hold because the existing link already owns its parent hold. Returning early also avoids changing an existing CIS back to BT_CONNECT.

Fixes 69997d50ec57 ("Bluetooth: ISO: handle bound CIS cleanup via hci_conn")
---
 net/bluetooth/hci_conn.c |    9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)
net/bluetooth/hci_conn.c +8 -1
@@ -1975,6 +1975,8 @@ struct hci_conn *hci_bind_cis(struct hci
 		cis->conn_timeout = timeout;
 	}
 
+	hci_conn_hold(cis);
+
 	if (cis->state == BT_CONNECTED)
 		return cis;
 
@@ -2016,7 +2018,6 @@ struct hci_conn *hci_bind_cis(struct hci
 		return ERR_PTR(-EINVAL);
 	}
 
-	hci_conn_hold(cis);
 	cis->state = BT_BOUND;
 
 	return cis;
@@ -2380,6 +2381,12 @@ struct hci_conn *hci_connect_cis(struct
 		hci_conn_drop(le);
 		return cis;
 	}
+
+	/* The existing link already owns the hold on its parent. */
+	if (cis->link) {
+		hci_conn_drop(le);
+		return cis;
+	}
 
 	link = hci_conn_link(le, cis);
 	hci_conn_drop(cis);
author maintainer
#322 lore raw

PATCH 322/395 Bluetooth: hci_sock: validate event length before filtering

In reply to Greg Kroah-Hartman

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

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

From: Aldo Ariel Panzardo <[email protected]>

commit b0a6cf99afd57a39598b1beca0e86ef5004980de upstream.

is_filtered_packet() reads the event code from skb->data[0] without first checking that the skb is nonempty. When an opcode filter is configured, it also reads the command opcode at offsets 3 or 4 without checking that a Command Complete or Command Status event is long enough.

hci_send_to_sock() invokes the filter before hci_event_packet() validates the event header. A malformed event supplied by a controller or a vhci device can therefore cause an out-of-bounds read.

Keep the unmasked event code for the opcode checks. The masked value is needed for the 64-bit event bitmap, but using it to identify command events aliases event codes above 0x3f. In particular, Synchronous Train Complete (0x4f) was treated as Command Status (0x0f) even though its payload has no opcode.

Reject actual command events that are too short for the field being inspected. A truncated command event cannot match a configured opcode.

---
 net/bluetooth/hci_sock.c |   17 ++++++++++++++---
 1 file changed, 14 insertions(+), 3 deletions(-)
net/bluetooth/hci_sock.c +14 -3
@@ -166,6 +166,7 @@ static bool is_filtered_packet(struct so
 {
 	struct hci_filter *flt;
 	int flt_type, flt_event;
+	u8 event;
 
 	/* Apply filter */
 	flt = &hci_pi(sk)->filter;
@@ -179,7 +180,11 @@ static bool is_filtered_packet(struct so
 	if (hci_skb_pkt_type(skb) != HCI_EVENT_PKT)
 		return false;
 
-	flt_event = (*(__u8 *)skb->data & HCI_FLT_EVENT_BITS);
+	if (skb->len < 1)
+		return true;
+
+	event = *(__u8 *)skb->data;
+	flt_event = event & HCI_FLT_EVENT_BITS;
 
 	if (!hci_test_bit(flt_event, &flt->event_mask))
 		return true;
@@ -188,11 +193,17 @@ static bool is_filtered_packet(struct so
 	if (!flt->opcode)
 		return false;
 
-	if (flt_event == HCI_EV_CMD_COMPLETE &&
+	if (event == HCI_EV_CMD_COMPLETE && skb->len < 5)
+		return true;
+
+	if (event == HCI_EV_CMD_COMPLETE &&
 	    flt->opcode != get_unaligned((__le16 *)(skb->data + 3)))
 		return true;
 
-	if (flt_event == HCI_EV_CMD_STATUS &&
+	if (event == HCI_EV_CMD_STATUS && skb->len < 6)
+		return true;
+
+	if (event == HCI_EV_CMD_STATUS &&
 	    flt->opcode != get_unaligned((__le16 *)(skb->data + 4)))
 		return true;
 
author maintainer
#323 lore raw

PATCH 323/395 Bluetooth: hci_sock: reject out-of-range OCF values

In reply to Greg Kroah-Hartman

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

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

From: Aldo Ariel Panzardo <[email protected]>

commit e93fad891c72deb84cae49430163b384ebcc92b1 upstream.

The raw HCI socket security filter has 128 OCF bits per supported OGF, but masks the 10-bit OCF with 127 before looking up the command. An unprivileged socket can therefore submit a reserved OCF that aliases an allowlisted command modulo 128.

A conforming controller should reject reserved opcodes. Nevertheless, the security decision must apply to the opcode that will actually be sent, especially since controller-specific behavior is outside the host stack's control.

Reject OCF values that cannot be represented by the security filter instead of aliasing them onto an unrelated command.

---
 net/bluetooth/hci_sock.c |    3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
net/bluetooth/hci_sock.c +2 -1
@@ -1894,7 +1894,8 @@ static int hci_sock_sendmsg(struct socke
 		u16 ocf = hci_opcode_ocf(opcode);
 
 		if (((ogf > HCI_SFLT_MAX_OGF) ||
-		     !hci_test_bit(ocf & HCI_FLT_OCF_BITS,
+		     (ocf > HCI_FLT_OCF_BITS) ||
+		     !hci_test_bit(ocf,
 				   &hci_sec_filter.ocf_mask[ogf])) &&
 		    !capable(CAP_NET_RAW)) {
 			err = -EPERM;
author maintainer
#324 lore raw

PATCH 324/395 Bluetooth: ISO: balance the parent hold in hci_bind_bis()

In reply to Greg Kroah-Hartman

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

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

From: Aldo Ariel Panzardo <[email protected]>

commit 4c94557dd02569efa6c1072a0439addaef9a5224 upstream.

hci_conn_link() takes a lifetime reference to its parent with hci_conn_get(), but only takes an operational hold on the child. hci_conn_unlink() later balances both a hold and a reference on the parent.

The SCO and CIS paths pass a parent acquired from a connect helper, so it already has a hold. For an additional BIS, hci_bind_bis() obtains the parent from hci_conn_hash_lookup_big(), which returns a bare pointer. Unlinking the child then drops the parent's existing hold and can schedule it for disconnection while its socket is still using it.

Take a hold on the parent before linking it and drop that hold if linking fails. A successful link transfers the hold to hci_conn_unlink().

Fixes fa224d0c094a ("Bluetooth: ISO: Reassociate a socket with an active BIS")
---
 net/bluetooth/hci_conn.c |    5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)
net/bluetooth/hci_conn.c +4 -1
@@ -2273,10 +2273,13 @@ struct hci_conn *hci_bind_bis(struct hci
 	parent = hci_conn_hash_lookup_big(hdev,
 					  conn->iso_qos.bcast.big);
 	if (parent && parent != conn) {
+		hci_conn_hold(parent);
 		link = hci_conn_link(parent, conn);
 		hci_conn_drop(conn);
-		if (!link)
+		if (!link) {
+			hci_conn_drop(parent);
 			return ERR_PTR(-ENOLINK);
+		}
 	}
 
 	return conn;
author maintainer
#325 lore raw

PATCH 325/395 Bluetooth: ISO: release unused CIS holds after channel attach

In reply to Greg Kroah-Hartman

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

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

From: Aldo Ariel Panzardo <[email protected]>

commit 0fcd4dad555c96e0bd3a1b8c569f989be85c7341 upstream.

hci_bind_cis() and hci_connect_cis() return one hci_conn hold for the ISO layer. A new channel association consumes that hold, which is eventually released by iso_conn_free().

There are two cases where iso_chan_add() does not create an association:
it returns success when the socket is already attached to the same iso_conn, and it returns -EBUSY when another socket is attached. The hold returned for the current call is unused in both cases. This occurs when deferred setup calls iso_connect_cis() again for its existing socket, or when another socket attempts to reuse the CIS.

Detect the idempotent case while the connection is locked and release the unused hold after iso_chan_add(). Also release it on -EBUSY. Do not drop it for other errors: a newly allocated iso_conn releases the transferred hold when its last temporary reference is put.

Fixes 69997d50ec57 ("Bluetooth: ISO: handle bound CIS cleanup via hci_conn")
---
 net/bluetooth/iso.c |    7 +++++++
 1 file changed, 7 insertions(+)
net/bluetooth/iso.c +7 -0
@@ -460,6 +460,7 @@ static int iso_connect_cis(struct sock *
 	struct hci_dev  *hdev;
 	bdaddr_t src, dst;
 	u8 src_type;
+	bool already_attached;
 	int err;
 
 	lock_sock(sk);
@@ -530,8 +531,14 @@ static int iso_connect_cis(struct sock *
 		goto unlock;
 	}
 
+	iso_conn_lock(conn);
+	already_attached = iso_pi(sk)->conn == conn && conn->sk == sk;
+	iso_conn_unlock(conn);
+
 	err = iso_chan_add(conn, sk, NULL);
 	iso_conn_put(conn);
+	if (already_attached || err == -EBUSY)
+		hci_conn_drop(hcon);
 	if (err)
 		goto unlock;
 
author maintainer
#326 lore raw

PATCH 326/395 Bluetooth: L2CAP: validate frame length before control and FCS access

In reply to Greg Kroah-Hartman

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

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

From: Aldo Ariel Panzardo <[email protected]>

commit 6c78a213d9070b610c7f418af2c25b66180b7e37 upstream.

l2cap_data_rcv() unpacks either a two-byte or four-byte control field without first ensuring that it is present. A short ERTM or streaming-mode frame can therefore cause an out-of-bounds read.

There is a second short-frame case when CRC16 is enabled. After the control field is pulled, l2cap_check_fcs() subtracts two from skb->len without checking it. If fewer than two bytes remain, the subtraction wraps; skb_trim() leaves the buffer unchanged and the subsequent FCS load reads past the logical end of the frame.

Validate that the frame contains both its control field and, when enabled, its FCS before either field is accessed.

Fixes 1c2acffb76d4 ("Bluetooth: Add initial support for ERTM packets transfers")
Fixes fcc203c30d72 ("Bluetooth: Add support for FCS option to L2CAP")
---
 net/bluetooth/l2cap_core.c |   10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)
net/bluetooth/l2cap_core.c +9 -1
@@ -6709,9 +6709,17 @@ static int l2cap_stream_rx(struct l2cap_
 static int l2cap_data_rcv(struct l2cap_chan *chan, struct sk_buff *skb)
 {
 	struct l2cap_ctrl *control = &bt_cb(skb)->l2cap;
-	u16 len;
+	u16 len, min_len;
 	u8 event;
 
+	min_len = test_bit(FLAG_EXT_CTRL, &chan->flags) ?
+		  L2CAP_EXT_CTRL_SIZE : L2CAP_ENH_CTRL_SIZE;
+	if (chan->fcs == L2CAP_FCS_CRC16)
+		min_len += L2CAP_FCS_SIZE;
+
+	if (skb->len < min_len)
+		goto drop;
+
 	__unpack_control(chan, skb);
 
 	len = skb->len;
author maintainer
#327 lore raw

PATCH 327/395 Bluetooth: mgmt: fix race in read_unconf_index_list()

In reply to Greg Kroah-Hartman

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

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

From: Aldo Ariel Panzardo <[email protected]>

commit b5dbb41b212c50c095a4dbee3017a84fe94f033b upstream.

read_unconf_index_list() counts unconfigured controllers before allocating its response, then checks the device flags again while filling it.

hci_dev_list_lock stabilizes list membership, but it does not serialize the per-device flags. During asynchronous controller setup, the worker can set HCI_UNCONFIGURED and clear HCI_SETUP between the two passes. A controller omitted from the allocation count can then become eligible for the fill pass, causing an out-of-bounds write to rp->index[].

Allocate space for every device on hci_dev_list. Since list membership cannot change while hci_dev_list_lock is held, the response remains large enough regardless of flag transitions. The reported count and response length still include only eligible unconfigured controllers.

Fixes 73d1df2a7a10 ("Bluetooth: Add support for Read Unconfigured Index List command")
---
 net/bluetooth/mgmt.c |    8 ++------
 1 file changed, 2 insertions(+), 6 deletions(-)
net/bluetooth/mgmt.c +2 -6
@@ -499,13 +499,9 @@ static int read_unconf_index_list(struct
 
 	read_lock(&hci_dev_list_lock);
 
-	count = 0;
-	list_for_each_entry(d, &hci_dev_list, list) {
-		if (hci_dev_test_flag(d, HCI_UNCONFIGURED))
-			count++;
-	}
+	count = list_count_nodes(&hci_dev_list);
 
-	rp_len = sizeof(*rp) + (2 * count);
+	rp_len = sizeof(*rp) + (sizeof(__le16) * count);
 	rp = kmalloc(rp_len, GFP_ATOMIC);
 	if (!rp) {
 		read_unlock(&hci_dev_list_lock);
author maintainer
#328 lore raw

PATCH 328/395 Bluetooth: RFCOMM: fix NULL dereference of dlc->session in RFCOMM_CONNINFO

In reply to Greg Kroah-Hartman

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

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

From: Hui Peng <[email protected]>

commit 46f8ffd0a1f1eb6cbc94946a92c11ef601e228a1 upstream.

The RFCOMM_CONNINFO getsockopt handler accepts a socket that is not connected as long as deferred setup is enabled:

	if (sk->sk_state != BT_CONNECTED &&
				!rfcomm_pi(sk)->dlc->defer_setup) {
		err = -ENOTCONN;
		break;
	}
	l2cap_sk = rfcomm_pi(sk)->dlc->session->sock->sk;

dlc->defer_setup is set in rfcomm_sock_init() when rfcomm_connect_ind() creates a child socket for an incoming connection on a listening socket that has BT_DEFER_SETUP enabled. It is never cleared afterwards. The session, however, can go away underneath it.

rfcomm_recv_disc() forces the dlc state before tearing it down:

	d->state = BT_CLOSED;
	__rfcomm_dlc_close(d, err);

The RFCOMM_DEFER_SETUP early return in __rfcomm_dlc_close() only covers BT_CONNECT, BT_CONFIG, BT_OPEN and BT_CONNECT2, so with the state already BT_CLOSED that switch does not match and the function falls through to rfcomm_dlc_unlink(), which sets d->session = NULL, while d->defer_setup stays 1.

A getsockopt(SOL_RFCOMM, RFCOMM_CONNINFO) on the accepted socket after that point therefore skips the -ENOTCONN path -- sk->sk_state is BT_CLOSED, but dlc->defer_setup is still set -- and dereferences the NULL session. No race is needed: once the DISC has been processed, the dereference is unconditional.

Reproduced on a KASAN kernel under QEMU with a BR/EDR peer emulated over /dev/vhci: the peer brings up an ACL link, opens L2CAP on the RFCOMM PSM, starts a session and sends SABM for a channel bound with BT_DEFER_SETUP, and sends DISC for that dlci after the socket has been accepted. getsockopt(SOL_RFCOMM, RFCOMM_CONNINFO) on the accepted socket then hits:

 Oops: general protection fault, probably for non-canonical address
 0xdffffc0000000002: 0000 [#1] SMP KASAN PTI
 KASAN: null-ptr-deref in range [0x0000000000000010-0x0000000000000017]
 CPU: 1 UID: 0 PID: 150 Comm: init Tainted: G B 7.3.0-rc3-g5dd1818b15d9
 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996)
 RIP: 0010:rfcomm_sock_getsockopt+0x529/0x780
 Call Trace:
  <TASK>
  do_sock_getsockopt+0x3ad/0x7d0
  __sys_getsockopt+0x10e/0x1b0
  __x64_sys_getsockopt+0xc2/0x160
  do_syscall_64+0xda/0x4b0
  entry_SYSCALL_64_after_hwframe+0x77/0x7f
  </TASK>

0x10 is the offset of sock in struct rfcomm_session; rfcomm_sock_getsockopt_old() is inlined into rfcomm_sock_getsockopt().

Commit 43a556b2fd43 ("Bluetooth: RFCOMM: take rfcomm_mutex for the deferred setup accept") fixed the same "a remote DISC clears the session while deferred setup is still flagged" problem in rfcomm_dlc_accept(); this is the remaining instance of it, in the getsockopt path.

Deferred setup only leaves a socket usable here once it has reached BT_CONNECT2, so restrict the exception to that state and check that a session is actually present before following it.

Fixes bb23c0ab8246 ("Bluetooth: Add support for deferring RFCOMM connection setup")
Assisted-by LLM
---
 net/bluetooth/rfcomm/sock.c |    6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)
net/bluetooth/rfcomm/sock.c +4 -2
@@ -785,8 +785,10 @@ static int rfcomm_sock_getsockopt_old(st
 		break;
 
 	case RFCOMM_CONNINFO:
-		if (sk->sk_state != BT_CONNECTED &&
-					!rfcomm_pi(sk)->dlc->defer_setup) {
+		if ((sk->sk_state != BT_CONNECTED &&
+		     !(sk->sk_state == BT_CONNECT2 &&
+		       rfcomm_pi(sk)->dlc->defer_setup)) ||
+		    !rfcomm_pi(sk)->dlc->session) {
 			err = -ENOTCONN;
 			break;
 		}
author maintainer
#329 lore raw

PATCH 329/395 smb: client: fix create context out-of-bounds reads

In reply to Greg Kroah-Hartman

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

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

From: Zihan Xi <[email protected]>

commit 67f4c1c6a1b51e203d986779299824d1c2c590a6 upstream.

smb2_parse_contexts() validates the complete create-context area but does not limit each record to its Next field before dispatching it. A malformed chain can therefore expose bytes beyond the current context to a handler. The QFid handler also used a full response-structure cast although it only reads DiskFileId.

The SMB2/SMB3 lease parsers made the same layout assumption: they read LeaseState and LeaseFlags at canonical offsets rather than at DataOffset. A valid non-canonical DataOffset could therefore yield unrelated in-bounds data, while a short DataLength was still accepted.

Limit each context to its Next value, reject offsets before the context header, and reject malformed chains. Bound the name range by the current context and do not dispatch a known handler when DataLength is zero. Read the QFid DiskFileId only when the context data covers that field. Parse the lease context from DataOffset and require DataLength to match the v1 or v2 lease_context size used by ksmbd. A size mismatch skips lease parsing without failing the open.

Fixes b8c32dbb0deb ("CIFS: Request SMB2.1 leases")
Fixes f047390a097e ("CIFS: Add create lease v2 context for SMB3")
Fixes 89a5bfa350fa ("smb3: optimize open to not send query file internal info")
Assisted-by LLM
---
 fs/smb/client/smb2ops.c |   28 ++++++++++++++++++++--------
 fs/smb/client/smb2pdu.c |   44 ++++++++++++++++++++++++++++++++++----------
 2 files changed, 54 insertions(+), 18 deletions(-)
fs/smb/client/smb2ops.c +20 -8
@@ -4457,25 +4457,37 @@ smb3_create_lease_buf(u8 *lease_key, u8
 static __u8
 smb2_parse_lease_buf(void *buf, __u16 *epoch, char *lease_key)
 {
-	struct create_lease *lc = (struct create_lease *)buf;
+	struct create_context *cc = buf;
+	struct lease_context lc;
 
 	*epoch = 0; /* not used */
-	if (lc->lcontext.LeaseFlags & SMB2_LEASE_FLAG_BREAK_IN_PROGRESS_LE)
+	if (le32_to_cpu(cc->DataLength) != sizeof(lc))
+		return 0;
+
+	memcpy(&lc, (u8 *)cc + le16_to_cpu(cc->DataOffset), sizeof(lc));
+	if (lc.LeaseFlags & SMB2_LEASE_FLAG_BREAK_IN_PROGRESS_LE)
 		return SMB2_OPLOCK_LEVEL_NOCHANGE;
-	return le32_to_cpu(lc->lcontext.LeaseState);
+	return le32_to_cpu(lc.LeaseState);
 }
 
 static __u8
 smb3_parse_lease_buf(void *buf, __u16 *epoch, char *lease_key)
 {
-	struct create_lease_v2 *lc = (struct create_lease_v2 *)buf;
+	struct create_context *cc = buf;
+	struct lease_context_v2 lc;
+
+	if (le32_to_cpu(cc->DataLength) != sizeof(lc)) {
+		*epoch = 0;
+		return 0;
+	}
 
-	*epoch = le16_to_cpu(lc->lcontext.Epoch);
-	if (lc->lcontext.LeaseFlags & SMB2_LEASE_FLAG_BREAK_IN_PROGRESS_LE)
+	memcpy(&lc, (u8 *)cc + le16_to_cpu(cc->DataOffset), sizeof(lc));
+	*epoch = le16_to_cpu(lc.Epoch);
+	if (lc.LeaseFlags & SMB2_LEASE_FLAG_BREAK_IN_PROGRESS_LE)
 		return SMB2_OPLOCK_LEVEL_NOCHANGE;
 	if (lease_key)
-		memcpy(lease_key, &lc->lcontext.LeaseKey, SMB2_LEASE_KEY_SIZE);
-	return le32_to_cpu(lc->lcontext.LeaseState);
+		memcpy(lease_key, lc.LeaseKey, SMB2_LEASE_KEY_SIZE);
+	return le32_to_cpu(lc.LeaseState);
 }
 
 static unsigned int
fs/smb/client/smb2pdu.c +34 -10

93 lines not on this page · raw message

author maintainer
#330 lore raw

PATCH 330/395 smb: client: clean up failed cached directory opens

In reply to Greg Kroah-Hartman

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

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

From: Zihan Xi <[email protected]>

commit d2ff5fb93ea83034025850266b5eed391f96b825 upstream.

open_cached_dir() sends CREATE and QUERY_INFO as a compound request. If the CREATE succeeds but a later command returns an error, the function must retain the CREATE FID so common cleanup can issue SMB2_close(). It also must not treat a response error as a valid CREATE.

Validate the CREATE response before using its fields, record the FIDs, and mark the handle open before handling errors from later compound commands. Move the -EREMCHG reconnect handling before response validation so a missing response does not hide the reconnect request. Count the handle when it is marked open; confirmed close responses decrement the counter, while existing close retry behavior remains best effort on transport failures.

Fixes b0f6df737a1c ("cifs: cache FILE_ALL_INFO for the shared root handle")
Assisted-by LLM
---
 fs/smb/client/cached_dir.c |   32 ++++++++++++++++++++++----------
 1 file changed, 22 insertions(+), 10 deletions(-)
fs/smb/client/cached_dir.c +22 -10
@@ -8,6 +8,7 @@
 #include <linux/namei.h>
 #include "cifsglob.h"
 #include "cifsproto.h"
+#include "../common/smb2status.h"
 #include "cifs_debug.h"
 #include "smb2proto.h"
 #include "cached_dir.h"
@@ -319,25 +320,37 @@ replay_again:
 	rc = compound_send_recv(xid, ses, server,
 				flags, 2, rqst,
 				resp_buftype, rsp_iov);
-	if (rc) {
-		if (rc == -EREMCHG) {
-			tcon->need_reconnect = true;
-			pr_warn_once("server share %s deleted\n",
-				     tcon->tree_name);
-		}
-		goto oshr_free;
+	if (rc == -EREMCHG) {
+		tcon->need_reconnect = true;
+		pr_warn_once("server share %s deleted\n",
+			     tcon->tree_name);
 	}
-	cfid->is_open = true;
 
-	spin_lock(&cfids->cfid_list_lock);
+	if (!rsp_iov[0].iov_base || rsp_iov[0].iov_len < sizeof(*o_rsp)) {
+		if (!rc)
+			rc = -EIO;
+		goto oshr_free;
+	}
 
 	o_rsp = (struct smb2_create_rsp *)rsp_iov[0].iov_base;
+	if (o_rsp->hdr.Status != STATUS_SUCCESS) {
+		if (!rc)
+			rc = -EIO;
+		goto oshr_free;
+	}
+
 	oparms.fid->persistent_fid = o_rsp->PersistentFileId;
 	oparms.fid->volatile_fid = o_rsp->VolatileFileId;
 #ifdef CONFIG_CIFS_DEBUG2
 	oparms.fid->mid = le64_to_cpu(o_rsp->hdr.MessageId);
 #endif /* CIFS_DEBUG2 */
+	cfid->is_open = true;
+	atomic_inc(&tcon->num_remote_opens);
 
+	if (rc)
+		goto oshr_free;
+
+	spin_lock(&cfids->cfid_list_lock);
 
 	if (o_rsp->OplockLevel != SMB2_OPLOCK_LEVEL_LEASE) {
 		spin_unlock(&cfids->cfid_list_lock);
@@ -404,7 +417,6 @@ out:
 		close_cached_dir(cfid);
 	} else {
 		*ret_cfid = cfid;
-		atomic_inc(&tcon->num_remote_opens);
 	}
 	kfree(utf16_path);
 
author maintainer
#331 lore raw

PATCH 331/395 smb: client: preserve create-context parsing errors

In reply to Greg Kroah-Hartman

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

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

From: Zihan Xi <[email protected]>

commit 2e828035d5d736d904c238ae7ec3f77c0d6270bf upstream.

smb2_compound_op() saves the result from compound_send_recv() in tmp_rc. For SMB2_OP_OPEN_QUERY it then parses the CREATE contexts, but the final assignment of rc from tmp_rc discards a parsing error. A malformed create-context response can therefore be reported as successful to smb2_query_path_info().

Keep a create-context parsing error in tmp_rc so it survives per-command response processing and is returned to the caller.

Fixes b07687edee99 ("cifs: Improve SMB2+ stat() to work also without FILE_READ_ATTRIBUTES")
Assisted-by LLM
---
 fs/smb/client/smb2inode.c |    4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
fs/smb/client/smb2inode.c +3 -1
@@ -696,8 +696,10 @@ finished:
 		/* smb2_parse_contexts() fills idata->fi.IndexNumber */
 		rc = smb2_parse_contexts(server, &rsp_iov[0], &oparms->fid->epoch,
 					 oparms->fid->lease_key, &oplock, &idata->fi, NULL);
-		if (rc)
+		if (rc) {
 			cifs_dbg(VFS, "rc: %d parsing context of compound op\n", rc);
+			tmp_rc = rc;
+		}
 	}
 
 	for (i = 0; i < num_cmds; i++) {
author maintainer
#332 lore raw

PATCH 332/395 smb: client: validate POSIX create context length

In reply to Greg Kroah-Hartman

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

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

From: Zihan Xi <[email protected]>

commit fa2e9900dd2a3f5a1e7ef5a8c5e8d435feedbfcc upstream.

parse_posix_ctxt() reads the fixed nlink, reparse_tag, and mode fields before checking that the POSIX create context contains them. A short context can pass the generic checks and still make these fixed-width reads run past its declared data.

The current in-tree smb2_open_file() path passes a NULL posix pointer, so this handler is not reached on the ordinary open path. Still require the POSIX data to cover all three fields before reading them because the helper performs those unguarded reads. Keep the existing soft-failure behavior so malformed optional metadata does not fail the open.

Fixes 69dda3059e7a ("cifs: add SMB2_open() arg to return POSIX data")
Assisted-by LLM
---
 fs/smb/client/smb2pdu.c |    7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)
fs/smb/client/smb2pdu.c +5 -2
@@ -2297,12 +2297,15 @@ static void
 parse_posix_ctxt(struct create_context *cc, struct smb2_file_all_info *info,
 		 struct create_posix_rsp *posix)
 {
-	int sid_len;
 	u8 *beg = (u8 *)cc + le16_to_cpu(cc->DataOffset);
-	u8 *end = beg + le32_to_cpu(cc->DataLength);
+	u32 dlen = le32_to_cpu(cc->DataLength);
+	u8 *end = beg + dlen;
+	int sid_len;
 	u8 *sid;
 
 	memset(posix, 0, sizeof(*posix));
+	if (dlen < 3 * sizeof(__le32))
+		return;
 
 	posix->nlink = get_unaligned_le32(beg);
 	posix->reparse_tag = get_unaligned_le32(beg + 4);
author maintainer
#333 lore raw

PATCH 333/395 xfs: dont assert when XFS_SCRUB_TYPE_HEALTHY scans return corruption

In reply to Greg Kroah-Hartman

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

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

From: Darrick J. Wong <[email protected]>

commit afbccf99f7f82117cba9ad4b0b006692030f49e8 upstream.

XFS_SCRUB_TYPE_HEALTHY is a synthentic scrub type so that xfs_scrub can tell the kernel "Hey, I finished a scan and saw no problems" and have the kernel forget that it saw indirect evidence of corruption.

Unfortunately, as LOLLM points out, it's possible for the health system to record a new corruption just before xfs_scrub gets to XFS_SCRUB_TYPE_HEALTHY. In this case, the existing logic doesn't return early and instead wanders into unknown regions of type_to_health_flag and trips the assert because HEALTHY doesn't have a group assignment.

Fix the logic so that we always return early for a HEALTHY scrub type, even if we decide not to call xchk_mark_all_healthy.

Fixes a1f3e0cca41036 ("xfs: update health status if we get a clean bill of health")
Assisted-by LOLLM # finding obvious bugs
---
 fs/xfs/scrub/health.c |    6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)
fs/xfs/scrub/health.c +3 -3
@@ -202,9 +202,9 @@ xchk_update_health(
 	 * there's no sick flag defined for it, so we branch here ahead of the
 	 * mask check.
 	 */
-	if (sc->sm->sm_type == XFS_SCRUB_TYPE_HEALTHY &&
-	    !(sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)) {
-		xchk_mark_all_healthy(sc->mp);
+	if (sc->sm->sm_type == XFS_SCRUB_TYPE_HEALTHY) {
+		if (!(sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT))
+			xchk_mark_all_healthy(sc->mp);
 		return;
 	}
 
author maintainer
#334 lore raw

PATCH 334/395 xfs: fix attr fork block count checks in xrep_inode_blockcounts

In reply to Greg Kroah-Hartman

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

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

From: Darrick J. Wong <[email protected]>

commit bb991b7f79dd34cc5f24db0f736bf75630c970e7 upstream.

LOLLM points out that a file has an attr fork, it will call xchk_inode_count_blocks to set @ablocks to the number of fsblocks mapped by the attr fork; but then it'll compare @blocks (aka the count of fsblocks mapped by the data fork). We already checked that and we never do anything with @acount, so I think this is clearly a bug. Fix the comparison.

Fixes 2d295fe65776d1 ("xfs: repair inode records")
Assisted-by LOLLM # finding obvious bugs
---
 fs/xfs/scrub/inode_repair.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
fs/xfs/scrub/inode_repair.c +1 -1
@@ -1698,7 +1698,7 @@ xrep_inode_blockcounts(
 				&acount);
 		if (error)
 			return error;
-		if (count >= sc->mp->m_sb.sb_dblocks)
+		if (acount >= sc->mp->m_sb.sb_dblocks)
 			return -EFSCORRUPTED;
 		error = xrep_ino_ensure_extent_count(sc, XFS_ATTR_FORK,
 				nextents);
author maintainer
#335 lore raw

PATCH 335/395 xfs: release orphanage dir inode if chown fails

In reply to Greg Kroah-Hartman

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

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

From: Darrick J. Wong <[email protected]>

commit 1c32cdc986467eaffeedb6c5334852809555b82d upstream.

LOLLM points out that we leak the igrab'd reference to the orphanage directory inode if chowning it fails. Fix that.

Fixes 1e58a8ccf2597c ("xfs: move orphan files to the orphanage")
Assisted-by LOLLM # finding obvious bugs
---
 fs/xfs/scrub/orphanage.c |    6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)
fs/xfs/scrub/orphanage.c +5 -1
@@ -193,12 +193,16 @@ xrep_orphanage_create(
 	/* Make sure the orphanage is owned by root. */
 	error = xrep_chown_orphanage(sc, XFS_I(orphanage_inode));
 	if (error)
-		goto out_dput_orphanage;
+		goto out_rele_orphanage;
 
 	/* Stash the reference for later and bail out. */
 	sc->orphanage = XFS_I(orphanage_inode);
 	sc->orphanage_ilock_flags = 0;
+	orphanage_inode = NULL;
 
+out_rele_orphanage:
+	if (orphanage_inode)
+		xchk_irele(sc, XFS_I(orphanage_inode));
 out_dput_orphanage:
 	dput(orphanage_dentry);
 out_unlock_root:
author maintainer
#336 lore raw

PATCH 336/395 xfs: use correct jiffies comparison function in xchk_maybe_relax

In reply to Greg Kroah-Hartman

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

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

From: Darrick J. Wong <[email protected]>

commit 984aab2d905a8557fafb27cd9e8713d6d12b3437 upstream.

LOLLM points out that we're supposed to use time_after_eq, not a raw >= operation here, or else jiffies wraps can go unnoticed. Fix this.

Fixes 271557de7cbfde ("xfs: reduce the rate of cond_resched calls inside scrub")
Assisted-by LOLLM # finding obvious bugs
---
 fs/xfs/scrub/scrub.h |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
fs/xfs/scrub/scrub.h +1 -1
@@ -40,7 +40,7 @@ static inline int xchk_maybe_relax(struc
 		return 0;
 	widget->resched_nr = 0;
 
-	if (unlikely(widget->next_resched <= jiffies)) {
+	if (unlikely(time_after_eq(jiffies, widget->next_resched))) {
 		cond_resched();
 		widget->next_resched = XCHK_RELAX_NEXT;
 	}
author maintainer
#337 lore raw

PATCH 337/395 xfs: check padding field in xfs_ioc_commit_range

In reply to Greg Kroah-Hartman

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

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

From: Darrick J. Wong <[email protected]>

commit 3083ba8dde765a9ab2337f3db68d00724a6b1202 upstream.

LOLLM points out that we don't check the ioctl padding field here, so let's do that. I don't think there are many users yet since exchrange requires a new feature flag, so it's a good time to try to plug this hole.

Fixes 398597c3ef7fb1 ("xfs: introduce new file range commit ioctls")
Assisted-by LOLLM # finding obvious bugs
---
 fs/xfs/xfs_exchrange.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
fs/xfs/xfs_exchrange.c +1 -1
@@ -902,7 +902,7 @@ xfs_ioc_commit_range(
 
 	if (copy_from_user(&args, argp, sizeof(args)))
 		return -EFAULT;
-	if (args.flags & ~XFS_EXCHANGE_RANGE_ALL_FLAGS)
+	if (args.pad || (args.flags & ~XFS_EXCHANGE_RANGE_ALL_FLAGS))
 		return -EINVAL;
 	if (kern_f->magic != XCR_FRESH_MAGIC)
 		return -EBUSY;
author maintainer
#338 lore raw

PATCH 338/395 xfs: dont call xfs_exchange_range_finish for a dry run

In reply to Greg Kroah-Hartman

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

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

From: Darrick J. Wong <[email protected]>

commit 8fc18580ec17f90beac4c933fbe4c74dcd3b7f36 upstream.

LOLLM noticed that we strip file privileges and whatnot even for a dry run. We also shouldn't flush dirty data to disk or trim COW staging events for a dry run. Neither of those behaviors are allowed by the manpage, so fix that by exiting early on DRY_RUN in various functions.

Fixes 42672471f938cd ("xfs: bind together the front and back ends of the file range exchange code")
Assisted-by LOLLM # finding obvious bugs
---
 fs/xfs/xfs_exchrange.c |   11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)
fs/xfs/xfs_exchrange.c +8 -3
@@ -633,6 +633,9 @@ xfs_exchrange_prep(
 	if (error)
 		return error;
 
+	if (fxr->flags & XFS_EXCHANGE_RANGE_DRY_RUN)
+		return 0;
+
 	trace_xfs_exchrange_flush(fxr, ip1, ip2);
 
 	/* Flush the relevant ranges of both files. */
@@ -709,9 +712,11 @@ xfs_exchrange_contents(
 	 * other file write would do.  This may involve turning on support for
 	 * logged xattrs if either file has security capabilities.
 	 */
-	error = xfs_exchange_range_finish(fxr);
-	if (error)
-		goto out_unlock;
+	if (!(fxr->flags & XFS_EXCHANGE_RANGE_DRY_RUN)) {
+		error = xfs_exchange_range_finish(fxr);
+		if (error)
+			goto out_unlock;
+	}
 
 out_unlock:
 	xfs_iunlock2_io_mmap(ip1, ip2);
author maintainer
#339 lore raw

PATCH 339/395 xfs: use the correct reservations for rtrmap/refcount recovery

In reply to Greg Kroah-Hartman

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

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

From: Darrick J. Wong <[email protected]>

commit 471e0b6e2ddac9e16b8dc2153d6e5575fefb8a2f upstream.

LOLLM noticed that we might reserve the wrong number of blocks for recovering rtrmap and rtrefcount updates after a crash. Fix that.

Fixes 5e0679d1c62f25 ("xfs: support recovering rmap intent items targetting realtime extents")
Assisted-by LOLLM # finding obvious bugs
---
 fs/xfs/xfs_refcount_item.c |    8 ++++++--
 fs/xfs/xfs_rmap_item.c     |    8 ++++++--
 2 files changed, 12 insertions(+), 4 deletions(-)
fs/xfs/xfs_refcount_item.c +6 -2
@@ -510,6 +510,7 @@ xfs_refcount_recover_work(
 	struct xfs_cui_log_item		*cuip = CUI_ITEM(lip);
 	struct xfs_trans		*tp;
 	struct xfs_mount		*mp = lip->li_log->l_mp;
+	unsigned int			dblocks;
 	bool				isrt = xfs_cui_item_isrt(lip);
 	int				i;
 	int				error = 0;
@@ -545,8 +546,11 @@ xfs_refcount_recover_work(
 	 * full btree split on either end of the refcount range.
 	 */
 	resv = xlog_recover_resv(&M_RES(mp)->tr_itruncate);
-	error = xfs_trans_alloc(mp, &resv, mp->m_refc_maxlevels * 2, 0,
-			XFS_TRANS_RESERVE, &tp);
+	if (isrt)
+		dblocks = mp->m_rtrefc_maxlevels * 2;
+	else
+		dblocks = mp->m_refc_maxlevels * 2;
+	error = xfs_trans_alloc(mp, &resv, dblocks, 0, XFS_TRANS_RESERVE, &tp);
 	if (error)
 		return error;
 
fs/xfs/xfs_rmap_item.c +6 -2
@@ -575,6 +575,7 @@ xfs_rmap_recover_work(
 	struct xfs_rui_log_item		*ruip = RUI_ITEM(lip);
 	struct xfs_trans		*tp;
 	struct xfs_mount		*mp = lip->li_log->l_mp;
+	unsigned int			dblocks;
 	bool				isrt = xfs_rui_item_isrt(lip);
 	int				i;
 	int				error = 0;
@@ -598,8 +599,11 @@ xfs_rmap_recover_work(
 	}
 
 	resv = xlog_recover_resv(&M_RES(mp)->tr_itruncate);
-	error = xfs_trans_alloc(mp, &resv, mp->m_rmap_maxlevels, 0,
-			XFS_TRANS_RESERVE, &tp);
+	if (isrt)
+		dblocks = mp->m_rtrmap_maxlevels;
+	else
+		dblocks = mp->m_rmap_maxlevels;
+	error = xfs_trans_alloc(mp, &resv, dblocks, 0, XFS_TRANS_RESERVE, &tp);
 	if (error)
 		return error;
 
author maintainer
#340 lore raw

PATCH 340/395 xfs: check di_forkoff correctly in scrub

In reply to Greg Kroah-Hartman

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

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

From: Darrick J. Wong <[email protected]>

commit e9193f2f1ce32d02b9230094ffdbfab715ab6137 upstream.

The di_forkoff check in xchk_dinode is incorrect, according to LOLLM. XFS_DFORK_BOFF returns a byte count relative to the start of the literal area, not the start of the inode. Therefore, this check won't flag di_forkoff values that are larger than the literal area but not the inode size itself. Fix this check; sadly the old APTR code was correct.

Fixes 6b5d917780219d ("xfs: dont cast to char * for XFS_DFORK_*PTR macros")
Assisted-by LOLLM # finding obvious bugs
---
 fs/xfs/scrub/inode.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
fs/xfs/scrub/inode.c +1 -1
@@ -607,7 +607,7 @@ xchk_dinode(
 	}
 
 	/* di_forkoff */
-	if (XFS_DFORK_BOFF(dip) >= mp->m_sb.sb_inodesize)
+	if (dip->di_forkoff >= (XFS_LITINO(mp) >> 3))
 		xchk_ino_set_corrupt(sc, ino);
 	if (naextents != 0 && dip->di_forkoff == 0)
 		xchk_ino_set_corrupt(sc, ino);
author maintainer
#341 lore raw

PATCH 341/395 xfs: fix rtgroup repair estimations

In reply to Greg Kroah-Hartman

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

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

From: Darrick J. Wong <[email protected]>

commit 41c4c41cf6c44f98db2916e1781f537d9ba6461a upstream.

When I added online fsck for realtime reflink, I forgot to update xrep_calc_rtgroup_resblks to factor in the size of the refcount btree when it guesses how much space we need to start a repair. This hasn't been a huge problem in practice because there are few filesystems with
(a) realtime, (b) rtgroups, (c) reflink, and (d) no rmap. But let's fix this before someone stumbles upon it, especially since LOLLM flagged this for me.

Fixes 83ccffc489975d ("xfs: online repair of the realtime refcount btree")
Assisted-by LOLLM # finding obvious bugs
---
 fs/xfs/scrub/repair.c |   19 +++++++++++++++++--
 fs/xfs/scrub/trace.h  |   12 ++++++++----
 2 files changed, 25 insertions(+), 6 deletions(-)
fs/xfs/scrub/repair.c +17 -2
@@ -399,6 +399,7 @@ xrep_calc_rtgroup_resblks(
 	struct xfs_mount		*mp = sc->mp;
 	struct xfs_scrub_metadata	*sm = sc->sm;
 	uint64_t			usedlen;
+	xfs_extlen_t			refcbt_sz = 0;
 	xfs_extlen_t			rmapbt_sz = 0;
 
 	if (!(sm->sm_flags & XFS_SCRUB_IFLAG_REPAIR))
@@ -411,13 +412,27 @@ xrep_calc_rtgroup_resblks(
 	usedlen = xfs_rtbxlen_to_blen(mp, xfs_rtgroup_extents(mp, sm->sm_agno));
 	ASSERT(usedlen <= XFS_MAX_RGBLOCKS);
 
+	if (xfs_has_reflink(mp))
+		refcbt_sz = xfs_rtrefcountbt_calc_size(mp, usedlen);
+
 	if (xfs_has_rmapbt(mp))
 		rmapbt_sz = xfs_rtrmapbt_calc_size(mp, usedlen);
 
+	/*
+	 * Guess how many blocks we need to rebuild the rmapbt.  For
+	 * non-reflink filesystems we can't have more records than used blocks.
+	 * However, with reflink it's possible to have more than one rmap
+	 * record per rtgroup block.  We don't know how many rmaps there could
+	 * be in the rtgroup, so we start off with what we hope is an generous
+	 * over-estimation.
+	 */
+	if (refcbt_sz > 0 && rmapbt_sz > 0)
+		rmapbt_sz *= 2;
+
 	trace_xrep_calc_rtgroup_resblks_btsize(mp, sm->sm_agno, usedlen,
-			rmapbt_sz);
+			rmapbt_sz, refcbt_sz);
 
-	return rmapbt_sz;
+	return max(rmapbt_sz, refcbt_sz);
 }
 #endif /* CONFIG_XFS_RT */
 
fs/xfs/scrub/trace.h +8 -4
@@ -2376,25 +2376,29 @@ TRACE_EVENT(xrep_calc_ag_resblks_btsize,
 #ifdef CONFIG_XFS_RT
 TRACE_EVENT(xrep_calc_rtgroup_resblks_btsize,
 	TP_PROTO(struct xfs_mount *mp, xfs_rgnumber_t rgno,
-		 xfs_rgblock_t usedlen, xfs_rgblock_t rmapbt_sz),
-	TP_ARGS(mp, rgno, usedlen, rmapbt_sz),
+		 xfs_rgblock_t usedlen, xfs_rgblock_t rmapbt_sz,
+		 xfs_rgblock_t refcbt_sz),
+	TP_ARGS(mp, rgno, usedlen, rmapbt_sz, refcbt_sz),
 	TP_STRUCT__entry(
 		__field(dev_t, dev)
 		__field(xfs_rgnumber_t, rgno)
 		__field(xfs_rgblock_t, usedlen)
 		__field(xfs_rgblock_t, rmapbt_sz)
+		__field(xfs_rgblock_t, refcbt_sz)
 	),
 	TP_fast_assign(
 		__entry->dev = mp->m_super->s_dev;
 		__entry->rgno = rgno;
 		__entry->usedlen = usedlen;
 		__entry->rmapbt_sz = rmapbt_sz;
+		__entry->refcbt_sz = refcbt_sz;
 	),
-	TP_printk("dev %d:%d rgno 0x%x usedlen %u rmapbt %u",
+	TP_printk("dev %d:%d rgno 0x%x usedlen %u rmapbt %u refcountbt %u",
 		  MAJOR(__entry->dev), MINOR(__entry->dev),
 		  __entry->rgno,
 		  __entry->usedlen,
-		  __entry->rmapbt_sz)
+		  __entry->rmapbt_sz,
+		  __entry->refcbt_sz)
 );
 #endif /* CONFIG_XFS_RT */
 
author maintainer
#342 lore raw

PATCH 342/395 xfs: fix wild memcpy access when formatting ondisk rtrefcount btree roots

In reply to Greg Kroah-Hartman

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

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

From: Darrick J. Wong <[email protected]>

commit fe2f9135df43db849e74f03956d322ac20b59af7 upstream.

LOLLM noticed that the inode btree root formatting methods copy too many bytes -- there's only one set of keys in node blocks, not two. This causes memory corruption of whatever's beyond the buffers.

Fixes f0415af60f482a ("xfs: wire up a new metafile type for the realtime refcount")
Assisted-by LOLLM # finding obvious bugs
---
 fs/xfs/libxfs/xfs_rtrefcount_btree.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
fs/xfs/libxfs/xfs_rtrefcount_btree.c +2 -2
@@ -617,7 +617,7 @@ xfs_rtrefcountbt_from_disk(
 		fpp = xfs_rtrefcount_droot_ptr_addr(dblock, 1, maxrecs);
 		tpp = xfs_rtrefcount_broot_ptr_addr(mp, rblock, 1, rblocklen);
 		numrecs = be16_to_cpu(dblock->bb_numrecs);
-		memcpy(tkp, fkp, 2 * sizeof(*fkp) * numrecs);
+		memcpy(tkp, fkp, sizeof(*fkp) * numrecs);
 		memcpy(tpp, fpp, sizeof(*fpp) * numrecs);
 	} else {
 		frp = xfs_rtrefcount_droot_rec_addr(dblock, 1);
@@ -703,7 +703,7 @@ xfs_rtrefcountbt_to_disk(
 		fpp = xfs_rtrefcount_broot_ptr_addr(mp, rblock, 1, rblocklen);
 		tpp = xfs_rtrefcount_droot_ptr_addr(dblock, 1, maxrecs);
 		numrecs = be16_to_cpu(rblock->bb_numrecs);
-		memcpy(tkp, fkp, 2 * sizeof(*fkp) * numrecs);
+		memcpy(tkp, fkp, sizeof(*fkp) * numrecs);
 		memcpy(tpp, fpp, sizeof(*fpp) * numrecs);
 	} else {
 		frp = xfs_rtrefcount_rec_addr(rblock, 1);
author maintainer
#343 lore raw

PATCH 343/395 xfs: call xfs_dquot_set_prealloc_limits if we installed default rtb limits

In reply to Greg Kroah-Hartman

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

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

From: Darrick J. Wong <[email protected]>

commit 065f3ce5936e68da75f3dc18201d290073d78f3c upstream.

Now that we have quotas for the realtime volume, we also have precomputed watermark limits for the realtime block counts. These precomputations should be done any time we change the rtb limits, which means that xfs_qm_adjust_dqlimits needs to ensure that if we installed a default rtb limit.

Fixes 5dd70852b03901 ("xfs: create quota preallocation watermarks for realtime quota")
---
 fs/xfs/xfs_dquot.c |    8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)
fs/xfs/xfs_dquot.c +6 -2
@@ -139,10 +139,14 @@ xfs_qm_adjust_dqlimits(
 		dq->q_ino.softlimit = defq->ino.soft;
 	if (!dq->q_ino.hardlimit)
 		dq->q_ino.hardlimit = defq->ino.hard;
-	if (!dq->q_rtb.softlimit)
+	if (!dq->q_rtb.softlimit) {
 		dq->q_rtb.softlimit = defq->rtb.soft;
-	if (!dq->q_rtb.hardlimit)
+		prealloc = 1;
+	}
+	if (!dq->q_rtb.hardlimit) {
 		dq->q_rtb.hardlimit = defq->rtb.hard;
+		prealloc = 1;
+	}
 
 	if (prealloc)
 		xfs_dquot_set_prealloc_limits(dq);
author maintainer
#344 lore raw

PATCH 344/395 xfs: dont let hidden_space go negative in xfs_metafile_resv_init

In reply to Greg Kroah-Hartman

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

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

From: Darrick J. Wong <[email protected]>

commit 476582d754cdc5110f806001417fea6c77824c13 upstream.

LOLLM points out that if the amount of fdblocks that we can reserve for a metadata btree file goes below the space already used by that file, then the hidden_space subtraction can underflow, causing xfs_dec_fdblocks to subtract a huge amount of space. We never want the target to be less than the used sapce, so fix the logic that adjusts dblocks_avail downwards.

Also fix an error in the adjacent comment.

Fixes 1df8d75030b787 ("xfs: make metabtree reservations global")
Assisted-by LOLLM # finding obvious bugs
---
 fs/xfs/libxfs/xfs_metafile.c |    8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)
fs/xfs/libxfs/xfs_metafile.c +4 -4
@@ -292,14 +292,14 @@ xfs_metafile_resv_init(
 		goto out_unlock;
 
 	/*
-	 * Space taken by the per-AG metadata btrees are accounted on-disk as
-	 * used space.  We therefore only hide the space that is reserved but
-	 * not used by the trees.
+	 * Space taken by metadata btrees are accounted on-disk as used space.
+	 * We therefore only hide the space that is reserved but not used by
+	 * the trees.
 	 */
 	if (used > target)
 		target = used;
 	else if (target > dblocks_avail)
-		target = dblocks_avail;
+		target = max(dblocks_avail, used);
 	hidden_space = target - used;
 
 	error = xfs_dec_fdblocks(mp, hidden_space, true);
author maintainer
#345 lore raw

PATCH 345/395 xfs: drop dquot flush lock when we cant find a buffer to flush

In reply to Greg Kroah-Hartman

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

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

From: Darrick J. Wong <[email protected]>

commit ffb48dccce1960a9ea24463a2f3c21d124d6b672 upstream.

LOLLM noticed that xfs_qm_flush_one fails to drop the dquot flush lock if it can't grab the buffer associated with the dquot. Since there's no buffer, nobody else is going to drop the dqflock, so we need to do it ourselves.

Fixes ca378189fdfa89 ("xfs: convert quotacheck to attach dquot buffers")
Assisted-by LOLLM # finding obvious bugs
---
 fs/xfs/xfs_qm.c |   10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)
fs/xfs/xfs_qm.c +8 -2
@@ -1477,16 +1477,22 @@ xfs_qm_flush_one(
 
 	error = xfs_dquot_use_attached_buf(dqp, &bp);
 	if (error)
-		goto out_unlock;
+		goto out_dqflock;
 	if (!bp) {
 		error = -EFSCORRUPTED;
-		goto out_unlock;
+		goto out_dqflock;
 	}
 
 	error = xfs_qm_dqflush(dqp, bp);
 	if (!error)
 		xfs_buf_delwri_queue(bp, buffer_list);
 	xfs_buf_relse(bp);
+	mutex_unlock(&dqp->q_qlock);
+	xfs_qm_dqrele(dqp);
+	return error;
+
+out_dqflock:
+	xfs_dqfunlock(dqp);
 out_unlock:
 	xfs_dqunlock(dqp);
 	return error;
author maintainer
#346 lore raw

PATCH 346/395 xfs: fix blockgc group quota scanning when usrquota isnt enforced

In reply to Greg Kroah-Hartman

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

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

From: Darrick J. Wong <[email protected]>

commit f8f6382ff13109e19d0fc1d0224ff7d29641e56a upstream.

LOLLM noticed the copy-paste error here -- if user quotas aren't enforced but we're near the group quota limit, we fail to set FLAG_GID and hence we might not actually free any preallocations, causing unnecessary EDQUOT. Fix that.

Fixes c237dd7c709432 ("xfs: flush eof/cowblocks if we can't reserve quota for inode creation")
Assisted-by LOLLM # finding obvious bugs
---
 fs/xfs/xfs_icache.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
fs/xfs/xfs_icache.c +1 -1
@@ -1650,7 +1650,7 @@ xfs_blockgc_free_dquots(
 		do_work = true;
 	}
 
-	if (XFS_IS_UQUOTA_ENFORCED(mp) && gdqp && xfs_dquot_lowsp(gdqp)) {
+	if (XFS_IS_GQUOTA_ENFORCED(mp) && gdqp && xfs_dquot_lowsp(gdqp)) {
 		icw.icw_gid = make_kgid(mp->m_super->s_user_ns, gdqp->q_id);
 		icw.icw_flags |= XFS_ICWALK_FLAG_GID;
 		do_work = true;
author maintainer
#347 lore raw

PATCH 347/395 xfs: fix cursor and pointer handling when recovering iunlink buckets

In reply to Greg Kroah-Hartman

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

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

From: Darrick J. Wong <[email protected]>

commit 65f39d09d73718611cee40399179323b5d4ead00 upstream.

LOLLM pointed out a bug in xlog_recover_iunlink_bucket:

1. We don't null out prev_ip after releasing it, which can lead to UAF problems if the inodegc flush call in the loop fails.

at which point I noticed even more bugs:

2. If the inodegc flush inside the loop fails, we also leak @ip.

3. We set prev_agino to agino having already advanced agino, which results in inodes with i_prev_unlinked set to itself.

4. If we exit the bottom of the loop with prev_ip set, then prev_ip aliases ip and we also set its i_prev_unlinked to itself.

Bugs 3 and 4 introduce loops into the unlinked list, though these loops don't surface because we immediately flush each unlinked inode after loading it.

Fix all of these issues.

Fixes 04755d2e5821b3 ("xfs: refactor xlog_recover_process_iunlinks()")
Assisted-by LOLLM # finding obvious bugs
---
 fs/xfs/xfs_log_recover.c |   13 ++++++++-----
 1 file changed, 8 insertions(+), 5 deletions(-)
fs/xfs/xfs_log_recover.c +8 -5
@@ -2736,12 +2736,13 @@ xlog_recover_iunlink_bucket(
 {
 	struct xfs_mount	*mp = pag_mount(pag);
 	struct xfs_inode	*prev_ip = NULL;
-	struct xfs_inode	*ip;
 	xfs_agino_t		prev_agino, agino;
 	int			error = 0;
 
 	agino = be32_to_cpu(agi->agi_unlinked[bucket]);
 	while (agino != NULLAGINO) {
+		struct xfs_inode	*ip;
+
 		error = xfs_iget(mp, NULL, xfs_agino_to_ino(pag, agino), 0, 0,
 				&ip);
 		if (error)
@@ -2750,11 +2751,11 @@ xlog_recover_iunlink_bucket(
 		ASSERT(VFS_I(ip)->i_nlink == 0);
 		ASSERT(VFS_I(ip)->i_mode != 0);
 		xfs_iflags_clear(ip, XFS_IRECOVERY);
-		agino = ip->i_next_unlinked;
 
 		if (prev_ip) {
 			ip->i_prev_unlinked = prev_agino;
 			xfs_irele(prev_ip);
+			prev_ip = NULL;
 
 			/*
 			 * Ensure the inode is removed from the unlinked list
@@ -2766,18 +2767,20 @@ xlog_recover_iunlink_bucket(
 			 * complete.
 			 */
 			error = xfs_inodegc_flush(mp);
-			if (error)
-				break;
+			if (error) {
+				xfs_irele(ip);
+				return error;
+			}
 		}
 
 		prev_agino = agino;
+		agino = ip->i_next_unlinked;
 		prev_ip = ip;
 	}
 
 	if (prev_ip) {
 		int	error2;
 
-		ip->i_prev_unlinked = prev_agino;
 		xfs_irele(prev_ip);
 
 		error2 = xfs_inodegc_flush(mp);
author maintainer
#348 lore raw

PATCH 348/395 net: tls: fix silent data drop under pipe back-pressure

In reply to Greg Kroah-Hartman

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

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

From: Jakub Kicinski <[email protected]>

[ Upstream commit 7e7be31bfdb066c1c780dcd6b1224078fc54063f ]

tls_sw_splice_read() uses len when advancing rxm->offset / rxm->full_len after skb_splice_bits(), rather than copied (the actual number of bytes successfully spliced into the pipe). When the destination pipe cannot accept all the requested bytes, splice_to_pipe() returns fewer bytes than len, and 'len - copied' of data is effectively skipped over.

Fixes e062fe99cccd ("tls: splice_read: fix accessing pre-processed records")
---
 net/tls/tls_sw.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)
net/tls/tls_sw.c +3 -3
index ea78e2ce754fb..ad6c0d61d5474 100644
@@ -2372,9 +2372,9 @@ ssize_t tls_sw_splice_read(struct socket *sock,  loff_t *ppos,
 	if (copied < 0)
 		goto splice_requeue;
 
-	if (chunk < rxm->full_len) {
-		rxm->offset += len;
-		rxm->full_len -= len;
+	if (copied < rxm->full_len) {
+		rxm->offset += copied;
+		rxm->full_len -= copied;
 		goto splice_requeue;
 	}
 
Signature · 1 line
2.53.0
author maintainer
#349 lore raw

PATCH 349/395 cgroup/cpuset: Move up prstate_housekeeping_conflict() helper

In reply to Greg Kroah-Hartman

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

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

From: Waiman Long <[email protected]>

[ Upstream commit 6cfeddbf4ade9202849d75c27c4d0c82b42c73d1 ]

Move up the prstate_housekeeping_conflict() helper so that it can be used in remote partition code.

Stable-dep-of: 31c88350b7dd ("cgroup/cpuset: Return PERR_NOCPUS in remote_partition_enable() on subpartitions_cpus conflict")

---
 kernel/cgroup/cpuset.c |   40 ++++++++++++++++++++--------------------
 1 file changed, 20 insertions(+), 20 deletions(-)
kernel/cgroup/cpuset.c +20 -20
@@ -1449,6 +1449,26 @@ static bool isolated_cpus_can_update(str
 	return res;
 }
 
+/*
+ * prstate_housekeeping_conflict - check for partition & housekeeping conflicts
+ * @prstate: partition root state to be checked
+ * @new_cpus: cpu mask
+ * Return: true if there is conflict, false otherwise
+ *
+ * CPUs outside of boot_hk_cpus, if defined, can only be used in an
+ * isolated partition.
+ */
+static bool prstate_housekeeping_conflict(int prstate, struct cpumask *new_cpus)
+{
+	if (!have_boot_isolcpus)
+		return false;
+
+	if ((prstate != PRS_ISOLATED) && !cpumask_subset(new_cpus, boot_hk_cpus))
+		return true;
+
+	return false;
+}
+
 static void update_isolation_cpumasks(bool isolcpus_updated)
 {
 	int ret;
@@ -1751,26 +1771,6 @@ invalidate:
 	remote_partition_disable(cs, tmp);
 }
 
-/*
- * prstate_housekeeping_conflict - check for partition & housekeeping conflicts
- * @prstate: partition root state to be checked
- * @new_cpus: cpu mask
- * Return: true if there is conflict, false otherwise
- *
- * CPUs outside of boot_hk_cpus, if defined, can only be used in an
- * isolated partition.
- */
-static bool prstate_housekeeping_conflict(int prstate, struct cpumask *new_cpus)
-{
-	if (!have_boot_isolcpus)
-		return false;
-
-	if ((prstate != PRS_ISOLATED) && !cpumask_subset(new_cpus, boot_hk_cpus))
-		return true;
-
-	return false;
-}
-
 /**
  * update_parent_effective_cpumask - update effective_cpus mask of parent cpuset
  * @cs:      The cpuset that requests change in partition root state
author maintainer
#350 lore raw

PATCH 350/395 cgroup/cpuset: Ensure domain isolated CPUs stay in root or isolated partition

In reply to Greg Kroah-Hartman

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

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

From: Waiman Long <[email protected]>

[ Upstream commit b1034a690129acd8995137bf4462470b4a2aa690 ]

Commit 4a74e418881f ("cgroup/cpuset: Check partition conflict with housekeeping setup") is supposed to ensure that domain isolated CPUs designated by the "isolcpus" boot command line option stay either in root partition or in isolated partitions. However, the required check wasn't implemented when a remote partition was created or when an existing partition changed type from "root" to "isolated".

Even though this is a relatively minor issue, we still need to add the required prstate_housekeeping_conflict() call in the right places to ensure that the rule is strictly followed.

The following steps can be used to reproduce the problem before this fix.

  # fmt -1 /proc/cmdline | grep isolcpus
  isolcpus=9
  # cd /sys/fs/cgroup/
  # echo +cpuset > cgroup.subtree_control
  # mkdir test
  # echo 9 > test/cpuset.cpus
  # echo isolated > test/cpuset.cpus.partition
  # cat test/cpuset.cpus.partition
  isolated
  # cat test/cpuset.cpus.effective
  9
  # echo root > test/cpuset.cpus.partition
  # cat test/cpuset.cpus.effective
  9
  # cat test/cpuset.cpus.partition
  root

With this fix, the last few steps will become:

  # echo root > test/cpuset.cpus.partition
  # cat test/cpuset.cpus.effective
  0-8,10-95
  # cat test/cpuset.cpus.partition
  root invalid (partition config conflicts with housekeeping setup)

Stable-dep-of: 31c88350b7dd ("cgroup/cpuset: Return PERR_NOCPUS in remote_partition_enable() on subpartitions_cpus conflict")

---
 kernel/cgroup/cpuset.c |   10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)
kernel/cgroup/cpuset.c +6 -4
@@ -1627,8 +1627,9 @@ static int remote_partition_enable(struc
 	if (!cpumask_intersects(tmp->new_cpus, cpu_active_mask) ||
 	    cpumask_subset(top_cpuset.effective_cpus, tmp->new_cpus))
 		return PERR_INVCPUS;
-	if ((new_prs == PRS_ISOLATED) &&
-	    !isolated_cpus_can_update(tmp->new_cpus, NULL))
+	if (((new_prs == PRS_ISOLATED) &&
+	     !isolated_cpus_can_update(tmp->new_cpus, NULL)) ||
+	    prstate_housekeeping_conflict(new_prs, tmp->new_cpus))
 		return PERR_HKEEPING;
 
 	spin_lock_irq(&callback_lock);
@@ -3109,8 +3110,9 @@ static int update_prstate(struct cpuset
 		 * A change in load balance state only, no change in cpumasks.
 		 * Need to update isolated_cpus.
 		 */
-		if ((new_prs == PRS_ISOLATED) &&
-		    !isolated_cpus_can_update(cs->effective_xcpus, NULL))
+		if (((new_prs == PRS_ISOLATED) &&
+		     !isolated_cpus_can_update(cs->effective_xcpus, NULL)) ||
+		    prstate_housekeeping_conflict(new_prs, cs->effective_xcpus))
 			err = PERR_HKEEPING;
 		else
 			isolcpus_updated = true;
author maintainer
#351 lore raw

PATCH 351/395 cgroup/cpuset: Return PERR_NOCPUS in remote_partition_enable() on subpartitions_cpus conflict

In reply to Greg Kroah-Hartman

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

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

From: Hui Peng <[email protected]>

[ Upstream commit 31c88350b7dd1522792f726f79607f31bb55c50f ]

When a remote partition is created underneath an existing local partition via a non-partition (PRS_MEMBER) intermediate cgroup, update_prstate() sees parent->partition_root_state == PRS_MEMBER and calls remote_partition_enable().

Commit 86888c7bd117 ("cgroup/cpuset: Add warnings to catch inconsistency in exclusive CPUs") replaced the cpumask_intersects(tmp->new_cpus, subpartitions_cpus) error check in remote_partition_enable() with WARN_ON_ONCE(). As a result, remote_partition_enable() emits a warning and proceeds to enable the remote partition on CPUs that are already owned by the ancestor local partition in subpartitions_cpus.

This can be reproduced on Linux 7.3.0-rc3 with:

mkdir -p /tmp/cg1
mount -t cgroup2 none /tmp/cg1
echo "+cpuset" > /tmp/cg1/cgroup.subtree_control

mkdir /tmp/cg1/A
echo 1 > /tmp/cg1/A/cpuset.cpus
echo 1 > /tmp/cg1/A/cpuset.cpus.exclusive echo root > /tmp/cg1/A/cpuset.cpus.partition echo "+cpuset" > /tmp/cg1/A/cgroup.subtree_control

mkdir /tmp/cg1/A/B
echo 1 > /tmp/cg1/A/B/cpuset.cpus
echo 1 > /tmp/cg1/A/B/cpuset.cpus.exclusive echo "+cpuset" > /tmp/cg1/A/B/cgroup.subtree_control

mkdir /tmp/cg1/A/B/D
echo 1 > /tmp/cg1/A/B/D/cpuset.cpus
echo 1 > /tmp/cg1/A/B/D/cpuset.cpus.exclusive echo root > /tmp/cg1/A/B/D/cpuset.cpus.partition

which triggers:

  WARNING: kernel/cgroup/cpuset.c:1594 at remote_partition_enable+0x1c1/0x300

and leaves both /tmp/cg1/A and /tmp/cg1/A/B/D as active root partitions claiming exclusive CPU 1.

Fix this by returning PERR_NOCPUS when tmp->new_cpus intersects subpartitions_cpus in remote_partition_enable(), matching the error code used by remote_cpus_update() for the same subpartitions_cpus conflict, and add a regression test case to tools/testing/selftests/cgroup/test_cpuset_prs.sh.

Tested in QEMU on Linux 7.3.0-rc3 using the reproducer above and tools/testing/selftests/cgroup/test_cpuset_prs.sh.

Fixes 86888c7bd117 ("cgroup/cpuset: Add warnings to catch inconsistency in exclusive CPUs")
Assisted-by LLM
---
 kernel/cgroup/cpuset.c                            |    3 ++-
 tools/testing/selftests/cgroup/test_cpuset_prs.sh |    2 ++
 2 files changed, 4 insertions(+), 1 deletion(-)
kernel/cgroup/cpuset.c +2 -1
@@ -1623,10 +1623,11 @@ static int remote_partition_enable(struc
 	 * above it or remote partition root underneath it is not allowed.
 	 */
 	compute_excpus(cs, tmp->new_cpus);
-	WARN_ON_ONCE(cpumask_intersects(tmp->new_cpus, subpartitions_cpus));
 	if (!cpumask_intersects(tmp->new_cpus, cpu_active_mask) ||
 	    cpumask_subset(top_cpuset.effective_cpus, tmp->new_cpus))
 		return PERR_INVCPUS;
+	if (cpumask_intersects(tmp->new_cpus, subpartitions_cpus))
+		return PERR_NOCPUS;
 	if (((new_prs == PRS_ISOLATED) &&
 	     !isolated_cpus_can_update(tmp->new_cpus, NULL)) ||
 	    prstate_housekeeping_conflict(new_prs, tmp->new_cpus))
tools/testing/selftests/cgroup/test_cpuset_prs.sh +2 -0
@@ -297,6 +297,8 @@ TEST_MATRIX=(
 	" C0-4:X2-4:S+ C1-4:X2-4:S+:P2 C2-4:X4:P1 \
 				   .      .      .      X1      .    0 A1:0-1|A2:2-4|A3:2-4 \
 								       A1:P0|A2:P2|A3:P-1 2-4"
+	" CX1-3:P1 CX1-3  CX1-3    .      .      .      P1      .    0 A1:1-3|A2:1-3|A3:1-3 \
+								       A1:P1|A2:P0|A3:P-1"
 
 	# Remote partition offline tests
 	" C0-3:S+ C1-3:S+ C2-3     .    X2-3   X2-3 X2-3:P2:O2=0 .   0 A1:0-1|A2:1|A3:3 A1:P0|A3:P2 2-3"
author maintainer
#352 lore raw

PATCH 352/395 vlan: require the MAC header to be present in __vlan_insert_inner_tag()

In reply to Greg Kroah-Hartman

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

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

From: Xiang Mei <[email protected]>

[ Upstream commit ab888242fce4f16f6c4d4c6ec53939ad36aa3b3a ]

__vlan_insert_inner_tag() only guarantees head room via skb_cow_head(), never that mac_len bytes of MAC header are present. Its ETH_HLEN wrappers - __vlan_insert_tag() under skb_vlan_push(), and vlan_insert_tag() under validate_xmit_vlan() on the generic transmit path - therefore rewrite the first 16 bytes at skb->data: a 12-byte memmove plus two 2-byte stores at +12 and +14. No caller supplies the bound, while the pop helpers use skb_ensure_writable()/pskb_may_pull().

An IFF_TUN device has hard_header_len == 0, so packet_snd() accepts a one-byte AF_PACKET/SOCK_RAW frame. The first vlan push only sets a hwaccel tag; the next - clsact "action vlan push" or bpf_skb_vlan_push() - enters the helper with skb->len still 1. The head comes from skbuff_small_head without __GFP_ZERO, so each push drags bytes from beyond skb->tail into the frame. After three the one-byte send leaves as 13 bytes carrying 11 bytes of uninitialised slab:

0000: 5a b3 62 12 80 88 ff ff 00 b3 62 12 81

           `------------------------------'

only 0x5a was sent; the rest is slab, here the top 56 bits of a linear-map address

Require the MAC header the helper rewrites to be present, so such a frame is dropped rather than transmitted.

Fixes 1da177e4c3f4 ("Linux-2.6.12-rc2")
Reported-by [email protected]
[ adjusted context around skb_cow_head(skb, VLAN_HLEN) because the branch lacks upstream meta_len handling. ]
---
 include/linux/if_vlan.h |    3 +++
 1 file changed, 3 insertions(+)
include/linux/if_vlan.h +3 -0
@@ -357,6 +357,9 @@ static inline int __vlan_insert_inner_ta
 {
 	struct vlan_ethhdr *veth;
 
+	if (unlikely(!pskb_may_pull(skb, mac_len)))
+		return -EINVAL;
+
 	if (skb_cow_head(skb, VLAN_HLEN) < 0)
 		return -ENOMEM;
 
author maintainer
#353 lore raw

PATCH 353/395 i2c: qcom-geni: Fix hardcoded clock index in SE_GENI_CLK_SEL

In reply to Greg Kroah-Hartman

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

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

From: Viken Dadhaniya <[email protected]>

[ Upstream commit cb97bf3d4f91453b881acaf8e9f0cc47bb40b604 ]

qcom_geni_i2c_conf() writes a hardcoded 0 to SE_GENI_CLK_SEL, which selects an index from the hardware clock performance table. This always picks the first table entry regardless of the actual source clock configuration. On platforms where the matching entry is not at index 0, the wrong source clock divider is active and the I2C bus runs at an incorrect frequency.

Use geni_se_clk_freq_match() in geni_i2c_clk_map_idx() to find the performance table index for the source clock (32 MHz or 19.2 MHz). Store the resolved index in a new clk_idx field in geni_i2c_dev and write it to SE_GENI_CLK_SEL instead of the hardcoded 0.

Fixes 37692de5d523 ("i2c: i2c-qcom-geni: Add bus driver for the Qualcomm GENI I2C controller")
Cc <[email protected]> # v4.19+
[ Applied initialization error handling in geni_i2c_probe() because geni_i2c_resources_init() is absent. ]
---
 drivers/i2c/busses/i2c-qcom-geni.c |   38 ++++++++++++++++++++++++++++++++-----
 1 file changed, 33 insertions(+), 5 deletions(-)
drivers/i2c/busses/i2c-qcom-geni.c +33 -5
@@ -77,6 +77,9 @@ enum geni_i2c_err_code {
 #define XFER_TIMEOUT		HZ
 #define RST_TIMEOUT		HZ
 
+#define GENI_SE_CLK_32MHZ	(32 * HZ_PER_MHZ)
+#define GENI_SE_CLK_19P2MHZ	19200000UL
+
 struct geni_i2c_dev {
 	struct geni_se se;
 	u32 tx_wm;
@@ -91,6 +94,7 @@ struct geni_i2c_dev {
 	struct clk *core_clk;
 	u32 clk_freq_out;
 	const struct geni_i2c_clk_fld *clk_fld;
+	u32 clk_idx;
 	int suspended;
 	void *dma_buf;
 	size_t xfer_len;
@@ -165,19 +169,44 @@ static const struct geni_i2c_clk_fld gen
 static int geni_i2c_clk_map_idx(struct geni_i2c_dev *gi2c)
 {
 	const struct geni_i2c_clk_fld *itr;
+	unsigned long res_freq;
 
-	if (clk_get_rate(gi2c->se.clk) == 32 * HZ_PER_MHZ)
+	/*
+	 * Frequency counter tables are calibrated for a specific source
+	 * clock frequency and are not valid for any multiple of it
+	 * (e.g. 64 MHz, 128 MHz).
+	 * Use exact=true and verify res_freq matches req_freq literally
+	 * to reject harmonics: a 64 MHz clock that divides evenly to
+	 * 32 MHz would pass exact matching but produce double the intended
+	 * I2C frequency with these counter values.
+	 */
+	if (!geni_se_clk_freq_match(&gi2c->se, GENI_SE_CLK_32MHZ,
+				    &gi2c->clk_idx, &res_freq, true) &&
+	    res_freq == GENI_SE_CLK_32MHZ) {
 		itr = geni_i2c_clk_map_32mhz;
-	else
+	} else if (!geni_se_clk_freq_match(&gi2c->se, GENI_SE_CLK_19P2MHZ,
+					   &gi2c->clk_idx, &res_freq, true) &&
+		   res_freq == GENI_SE_CLK_19P2MHZ) {
 		itr = geni_i2c_clk_map_19p2mhz;
+	} else {
+		dev_err(gi2c->se.dev,
+			"Unsupported SE source clock: must be exactly 32 MHz or 19.2 MHz\n");
+		return -EINVAL;
+	}
 
 	while (itr->clk_freq_out != 0) {
 		if (itr->clk_freq_out == gi2c->clk_freq_out) {
 			gi2c->clk_fld = itr;
+			dev_dbg(gi2c->se.dev,
+				"I2C clk selected: freq: %u Hz, clk_idx: %u\n",
+				gi2c->clk_freq_out, gi2c->clk_idx);
 			return 0;
 		}
 		itr++;
 	}
+
+	dev_err(gi2c->se.dev, "Unsupported I2C output frequency %u Hz\n", gi2c->clk_freq_out);
+
 	return -EINVAL;
 }
 
@@ -186,7 +215,7 @@ static void qcom_geni_i2c_conf(struct ge
 	const struct geni_i2c_clk_fld *itr = gi2c->clk_fld;
 	u32 val;
 
-	writel_relaxed(0, gi2c->se.base + SE_GENI_CLK_SEL);
+	writel_relaxed(gi2c->clk_idx, gi2c->se.base + SE_GENI_CLK_SEL);
 
 	val = (itr->clk_div << CLK_DIV_SHFT) | SER_CLK_EN;
 	writel_relaxed(val, gi2c->se.base + GENI_SER_M_CLK_CFG);
@@ -823,8 +852,7 @@ static int geni_i2c_probe(struct platfor
 
 	ret = geni_i2c_clk_map_idx(gi2c);
 	if (ret)
-		return dev_err_probe(dev, ret, "Invalid clk frequency %d Hz\n",
-				     gi2c->clk_freq_out);
+		return ret;
 
 	gi2c->adap.algo = &geni_i2c_algo;
 	init_completion(&gi2c->done);
author maintainer
#354 lore raw

PATCH 354/395 drm/i915/psr: Disable Panel Replay on Dell XPS 14 DA14260 as a quirk

In reply to Greg Kroah-Hartman

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

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

From: Jouni Högander <[email protected]>

[ Upstream commit 45c77d4bf8d4d15453d709b9b828e498898e0751 ]

Add new quirk (QUIRK_DISABLE_PANEL_REPLAY) for disabling Panel Replay as quirk for problematic setups. Apply this newly added quirk on Dell XPS 14 DA14260 if specific panel model is installed.

We are observing problems with Dell XPS 14 DA14260. This device has certain LGD panel model which seems to be problematic. We have seen other LGD panel model with same OUI is working fine. Due to this we can't apply the quirk only based on panel OUI. There are also cases where same device model has differing panel model. We don't want to disable Panel Replay on such devices. Best we can do is to apply the quirk based on both device model and panel model.

Stable-dep-of: 5271d81f99dd ("drm/i915/quirks: Limit eDP rate to HBR2 on HP Pavilion Plus 14-ew1")

---
 drivers/gpu/drm/i915/display/intel_psr.c    |    7 +++++++
 drivers/gpu/drm/i915/display/intel_quirks.c |   17 ++++++++++++++++-
 drivers/gpu/drm/i915/display/intel_quirks.h |    1 +
 3 files changed, 24 insertions(+), 1 deletion(-)
drivers/gpu/drm/i915/display/intel_psr.c +7 -0
@@ -49,6 +49,7 @@
 #include "intel_hdmi.h"
 #include "intel_psr.h"
 #include "intel_psr_regs.h"
+#include "intel_quirks.h"
 #include "intel_snps_phy.h"
 #include "intel_step.h"
 #include "intel_vblank.h"
@@ -592,6 +593,12 @@ static void _panel_replay_init_dpcd(stru
 	if (intel_dp->mst_detect == DRM_DP_MST)
 		return;
 
+	if (intel_has_dpcd_quirk(intel_dp, QUIRK_DISABLE_PANEL_REPLAY)) {
+		drm_dbg_kms(display->drm,
+			    "Panel Replay support not currently available for this setup\n");
+		return;
+	}
+
 	ret = drm_dp_dpcd_read_data(&intel_dp->aux, DP_PANEL_REPLAY_CAP_SUPPORT,
 				    &intel_dp->pr_dpcd, sizeof(intel_dp->pr_dpcd));
 	if (ret < 0)
drivers/gpu/drm/i915/display/intel_quirks.c +16 -1
@@ -86,6 +86,14 @@ static void quirk_edp_limit_rate_hbr2(st
 	drm_info(display->drm, "Applying eDP Limit rate to HBR2 quirk\n");
 }
 
+static void quirk_disable_panel_replay(struct intel_dp *intel_dp)
+{
+	struct intel_display *display = to_intel_display(intel_dp);
+
+	intel_set_dpcd_quirk(intel_dp, QUIRK_DISABLE_PANEL_REPLAY);
+	drm_info(display->drm, "Applying disable Panel Replay quirk\n");
+}
+
 struct intel_quirk {
 	int device;
 	int subsystem_vendor;
@@ -251,7 +259,14 @@ static const struct intel_dpcd_quirk int
 		.sink_oui = SINK_OUI(0x38, 0xec, 0x11),
 		.hook = quirk_fw_sync_len,
 	},
-
+	/* Dell XPS 14 DA14260 */
+	{
+		.device = 0xb080,
+		.subsystem_vendor = 0x1028,
+		.subsystem_device = 0x0db9,
+		.sink_oui = SINK_OUI(0x00, 0x22, 0xb9),
+		.hook = quirk_disable_panel_replay,
+	},
 };
 
 void intel_init_quirks(struct intel_display *display)
drivers/gpu/drm/i915/display/intel_quirks.h +1 -0
@@ -21,6 +21,7 @@ enum intel_quirk_id {
 	QUIRK_NO_PPS_BACKLIGHT_POWER_HOOK,
 	QUIRK_FW_SYNC_LEN,
 	QUIRK_EDP_LIMIT_RATE_HBR2,
+	QUIRK_DISABLE_PANEL_REPLAY,
 };
 
 void intel_init_quirks(struct intel_display *display);
author maintainer
#355 lore raw

PATCH 355/395 drm/i915/psr: Fixes for Dell XPS DA14260 quirk

In reply to Greg Kroah-Hartman

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

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

From: Jouni Högander <[email protected]>

[ Upstream commit 1de647abdfda9dc307503d0a85152161850ba52c ]

Dell seems to be changing device ID even within same device model. Due to this we need to ignore device ID when applying quirk for Dell XPS 14 DA14260. Do this by adding DEVICE_ID_ANY and assign it to Dell XPS 14 DA14260 quirk. Also apply the quirk only for eDP Panel Replay.

Fixes 45c77d4bf8d4 ("drm/i915/psr: Disable Panel Replay on Dell XPS 14 DA14260 as a quirk")
Cc Mika Kahola <[email protected]>

Stable-dep-of: 5271d81f99dd ("drm/i915/quirks: Limit eDP rate to HBR2 on HP Pavilion Plus 14-ew1")

---
 drivers/gpu/drm/i915/display/intel_psr.c    |    3 ++-
 drivers/gpu/drm/i915/display/intel_quirks.c |   16 ++++++++++------
 drivers/gpu/drm/i915/display/intel_quirks.h |    2 +-
 3 files changed, 13 insertions(+), 8 deletions(-)
drivers/gpu/drm/i915/display/intel_psr.c +2 -1
@@ -593,7 +593,8 @@ static void _panel_replay_init_dpcd(stru
 	if (intel_dp->mst_detect == DRM_DP_MST)
 		return;
 
-	if (intel_has_dpcd_quirk(intel_dp, QUIRK_DISABLE_PANEL_REPLAY)) {
+	if (intel_dp_is_edp(intel_dp) &&
+	    intel_has_dpcd_quirk(intel_dp, QUIRK_DISABLE_EDP_PANEL_REPLAY)) {
 		drm_dbg_kms(display->drm,
 			    "Panel Replay support not currently available for this setup\n");
 		return;
drivers/gpu/drm/i915/display/intel_quirks.c +10 -6
@@ -86,11 +86,11 @@ static void quirk_edp_limit_rate_hbr2(st
 	drm_info(display->drm, "Applying eDP Limit rate to HBR2 quirk\n");
 }
 
-static void quirk_disable_panel_replay(struct intel_dp *intel_dp)
+static void quirk_disable_edp_panel_replay(struct intel_dp *intel_dp)
 {
 	struct intel_display *display = to_intel_display(intel_dp);
 
-	intel_set_dpcd_quirk(intel_dp, QUIRK_DISABLE_PANEL_REPLAY);
+	intel_set_dpcd_quirk(intel_dp, QUIRK_DISABLE_EDP_PANEL_REPLAY);
 	drm_info(display->drm, "Applying disable Panel Replay quirk\n");
 }
 
@@ -116,6 +116,8 @@ struct intel_dpcd_quirk {
 
 #define SINK_DEVICE_ID_ANY	SINK_DEVICE_ID(0, 0, 0, 0, 0, 0)
 
+#define DEVICE_ID_ANY		0
+
 /* For systems that don't have a meaningful PCI subdevice/subvendor ID */
 struct intel_dmi_quirk {
 	void (*hook)(struct intel_display *display);
@@ -261,11 +263,11 @@ static const struct intel_dpcd_quirk int
 	},
 	/* Dell XPS 14 DA14260 */
 	{
-		.device = 0xb080,
+		.device = DEVICE_ID_ANY,
 		.subsystem_vendor = 0x1028,
 		.subsystem_device = 0x0db9,
 		.sink_oui = SINK_OUI(0x00, 0x22, 0xb9),
-		.hook = quirk_disable_panel_replay,
+		.hook = quirk_disable_edp_panel_replay,
 	},
 };
 
@@ -277,7 +279,8 @@ void intel_init_quirks(struct intel_disp
 	for (i = 0; i < ARRAY_SIZE(intel_quirks); i++) {
 		struct intel_quirk *q = &intel_quirks[i];
 
-		if (d->device == q->device &&
+		if ((d->device == q->device ||
+		     q->device == DEVICE_ID_ANY) &&
 		    (d->subsystem_vendor == q->subsystem_vendor ||
 		     q->subsystem_vendor == PCI_ANY_ID) &&
 		    (d->subsystem_device == q->subsystem_device ||
@@ -300,7 +303,8 @@ void intel_init_dpcd_quirks(struct intel
 	for (i = 0; i < ARRAY_SIZE(intel_dpcd_quirks); i++) {
 		const struct intel_dpcd_quirk *q = &intel_dpcd_quirks[i];
 
-		if (d->device == q->device &&
+		if ((d->device == q->device ||
+		     q->device == DEVICE_ID_ANY) &&
 		    (d->subsystem_vendor == q->subsystem_vendor ||
 		     q->subsystem_vendor == PCI_ANY_ID) &&
 		    (d->subsystem_device == q->subsystem_device ||
drivers/gpu/drm/i915/display/intel_quirks.h +1 -1
@@ -21,7 +21,7 @@ enum intel_quirk_id {
 	QUIRK_NO_PPS_BACKLIGHT_POWER_HOOK,
 	QUIRK_FW_SYNC_LEN,
 	QUIRK_EDP_LIMIT_RATE_HBR2,
-	QUIRK_DISABLE_PANEL_REPLAY,
+	QUIRK_DISABLE_EDP_PANEL_REPLAY,
 };
 
 void intel_init_quirks(struct intel_display *display);
author maintainer
#356 lore raw

PATCH 356/395 drm/i915/psr: Disable PSR2 on Xiaomi Book Pro 14 2026 as a quirk

In reply to Greg Kroah-Hartman

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

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

From: Jouni Högander <[email protected]>

[ Upstream commit 5e79af5db00b2a5f4667aaf16cfe4ecb7759383b ]

Add new quirk (QUIRK_DISABLE_PSR2) for disabling PSR2 as a quirk for problematic setups. Apply this newly added quirk on Xiaomi Book Pro 14 2026.

v2: logging adjusted

Stable-dep-of: 5271d81f99dd ("drm/i915/quirks: Limit eDP rate to HBR2 on HP Pavilion Plus 14-ew1")

---
 drivers/gpu/drm/i915/display/intel_psr.c    |    3 +++
 drivers/gpu/drm/i915/display/intel_quirks.c |    9 +++++++++
 drivers/gpu/drm/i915/display/intel_quirks.h |    1 +
 3 files changed, 13 insertions(+)
drivers/gpu/drm/i915/display/intel_psr.c +3 -0
@@ -668,6 +668,9 @@ static void _psr_init_dpcd(struct intel_
 	intel_dp->psr.sink_sync_latency =
 		intel_dp_get_sink_sync_latency(intel_dp);
 
+	if (intel_has_quirk(display, QUIRK_DISABLE_PSR2))
+		return;
+
 	if (DISPLAY_VER(display) >= 9 &&
 	    intel_dp->psr_dpcd[0] >= DP_PSR2_WITH_Y_COORD_IS_SUPPORTED) {
 		bool y_req = intel_dp->psr_dpcd[1] &
drivers/gpu/drm/i915/display/intel_quirks.c +9 -0
@@ -94,6 +94,12 @@ static void quirk_disable_edp_panel_repl
 	drm_info(display->drm, "Applying disable Panel Replay quirk\n");
 }
 
+static void quirk_disable_psr2(struct intel_display *display)
+{
+	intel_set_quirk(display, QUIRK_DISABLE_PSR2);
+	drm_info(display->drm, "PSR2 support not currently available for this setup, applying disable PSR2 quirk\n");
+}
+
 struct intel_quirk {
 	int device;
 	int subsystem_vendor;
@@ -250,6 +256,9 @@ static struct intel_quirk intel_quirks[]
 
 	/* Dell XPS 13 7390 2-in-1 */
 	{ 0x8a52, 0x1028, 0x08b0, quirk_edp_limit_rate_hbr2 },
+
+	/* Xiaomi Book Pro 14 2026 */
+	{ 0xb081, 0x1d72, 0x2424, quirk_disable_psr2 },
 };
 
 static const struct intel_dpcd_quirk intel_dpcd_quirks[] = {
drivers/gpu/drm/i915/display/intel_quirks.h +1 -0
@@ -22,6 +22,7 @@ enum intel_quirk_id {
 	QUIRK_FW_SYNC_LEN,
 	QUIRK_EDP_LIMIT_RATE_HBR2,
 	QUIRK_DISABLE_EDP_PANEL_REPLAY,
+	QUIRK_DISABLE_PSR2,
 };
 
 void intel_init_quirks(struct intel_display *display);
author maintainer
#357 lore raw

PATCH 357/395 drm/i915/quirks: Limit eDP rate to HBR2 on HP Pavilion Plus 14-ew1

In reply to Greg Kroah-Hartman

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

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

From: Ankit Nautiyal <[email protected]>

[ Upstream commit 5271d81f99dd01d983d439930eb056952485e15e ]

The eDP panel on the HP Pavilion Plus Laptop 14-ew1xxx advertises HBR3 while leaving the TPS4 support bit clear. The output however flickers, once link is trained with HBR3.

Until commit 8c9006283e4b ("Revert "drm/i915/dp: Reject HBR3 when sink doesn't support TPS4"") such sinks were capped at HBR2 by the TPS4 check which incidentally kept this panel stable. That check was reverted because other panels legitimately need HBR3 without advertising TPS4, and the per-machine QUIRK_EDP_LIMIT_RATE_HBR2 was introduced to handle the affected machines instead.

Add the machine to the list of devices that need the QUIRK_EDP_LIMIT_RATE_HBR2.

Fixes 8c9006283e4b ("Revert "drm/i915/dp: Reject HBR3 when sink doesn't support TPS4"")
Cc <[email protected]> # v6.18+
(cherry picked from commit 550b703fdbb2a2022faa75b4b11ab135241afbd9)
---
 drivers/gpu/drm/i915/display/intel_quirks.c |    3 +++
 1 file changed, 3 insertions(+)
drivers/gpu/drm/i915/display/intel_quirks.c +3 -0
@@ -257,6 +257,9 @@ static struct intel_quirk intel_quirks[]
 	/* Dell XPS 13 7390 2-in-1 */
 	{ 0x8a52, 0x1028, 0x08b0, quirk_edp_limit_rate_hbr2 },
 
+	/* HP Pavilion Plus Laptop 14-ew1xxx */
+	{ 0x7d55, 0x103c, 0x8c31, quirk_edp_limit_rate_hbr2 },
+
 	/* Xiaomi Book Pro 14 2026 */
 	{ 0xb081, 0x1d72, 0x2424, quirk_disable_psr2 },
 };
author maintainer
#358 lore raw

PATCH 358/395 drm/amdgpu: Fix acpi device leak in amdgpu_acpi_enumerate_xcc()

In reply to Greg Kroah-Hartman

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

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

From: Wentao Liang <[email protected]>

[ Upstream commit a997baa61179b450bd55c4810c7ccfed3b753a54 ]

amdgpu_acpi_enumerate_xcc() looks up each XCC ACPI device with acpi_dev_get_first_match_dev(), which takes a reference to the device. The reference is dropped with acpi_dev_put() after the XCC info is initialized, but if the kzalloc_obj() allocation of the XCC info fails the function returns -ENOMEM without releasing the reference, leaking the last reference to the ACPI device.

Drop the ACPI device reference on the allocation failure path before returning.

Fixes 4d5275ab0b18 ("drm/amdgpu: Add parsing of acpi xcc objects")
(cherry picked from commit 9211ef48b31ec66999cf55e04d0cbc60cd855fd5)
[ adapted the hunk to the existing kzalloc() failure block with braces and DRM_ERROR() logging. ]
---
 drivers/gpu/drm/amd/amdgpu/amdgpu_acpi.c |    1 +
 1 file changed, 1 insertion(+)
drivers/gpu/drm/amd/amdgpu/amdgpu_acpi.c +1 -0
@@ -1118,6 +1118,7 @@ static int amdgpu_acpi_enumerate_xcc(voi
 				   GFP_KERNEL);
 		if (!xcc_info) {
 			DRM_ERROR("Failed to allocate memory for xcc info\n");
+			acpi_dev_put(acpi_dev);
 			return -ENOMEM;
 		}
 
author maintainer
#359 lore raw

PATCH 359/395 drm/client: Remove holds_console_lock parameter from suspend/resume

In reply to Greg Kroah-Hartman

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

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

From: Thomas Zimmermann <[email protected]>

[ Upstream commit 7910d69376cde30e5871970d97d1a2e360568474 ]

No caller of the client resume/suspend helpers holds the console lock. The last such cases were removed from radeon in the patch series at [1]. Now remove the related parameter and the TODO items.

v2:
- update placeholders for CONFIG_DRM_CLIENT=n

Stable-dep-of: 1fca688e9443 ("drm/client: fix restore of partially initialized client")

---
 drivers/gpu/drm/amd/amdgpu/amdgpu_device.c |    8 ++++----
 drivers/gpu/drm/clients/drm_fbdev_client.c |   14 ++++----------
 drivers/gpu/drm/clients/drm_log.c          |    4 ++--
 drivers/gpu/drm/drm_client_event.c         |   16 ++++++++--------
 drivers/gpu/drm/drm_modeset_helper.c       |    6 +++---
 drivers/gpu/drm/i915/i915_driver.c         |    6 +++---
 drivers/gpu/drm/nouveau/nouveau_display.c  |    4 ++--
 drivers/gpu/drm/radeon/radeon_device.c     |    4 ++--
 drivers/gpu/drm/xe/display/xe_display.c    |    6 +++---
 include/drm/drm_client.h                   |   14 ++------------
 include/drm/drm_client_event.h             |    8 ++++----
 11 files changed, 37 insertions(+), 53 deletions(-)
drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +4 -4
@@ -5315,7 +5315,7 @@ int amdgpu_device_suspend(struct drm_dev
 		dev_warn(adev->dev, "smart shift update failed\n");
 
 	if (notify_clients)
-		drm_client_dev_suspend(adev_to_drm(adev), false);
+		drm_client_dev_suspend(adev_to_drm(adev));
 
 	cancel_delayed_work_sync(&adev->delayed_init_work);
 
@@ -5445,7 +5445,7 @@ exit:
 	flush_delayed_work(&adev->delayed_init_work);
 
 	if (notify_clients)
-		drm_client_dev_resume(adev_to_drm(adev), false);
+		drm_client_dev_resume(adev_to_drm(adev));
 
 	amdgpu_ras_resume(adev);
 
@@ -6060,7 +6060,7 @@ int amdgpu_device_reinit_after_reset(str
 				if (r)
 					goto out;
 
-				drm_client_dev_resume(adev_to_drm(tmp_adev), false);
+				drm_client_dev_resume(adev_to_drm(tmp_adev));
 
 				/*
 				 * The GPU enters bad state once faulty pages
@@ -6395,7 +6395,7 @@ static void amdgpu_device_halt_activitie
 		 */
 		amdgpu_unregister_gpu_instance(tmp_adev);
 
-		drm_client_dev_suspend(adev_to_drm(tmp_adev), false);
+		drm_client_dev_suspend(adev_to_drm(tmp_adev));
 
 		/* disable ras on ALL IPs */
 		if (!need_emergency_restart && !amdgpu_reset_in_dpc(adev) &&
drivers/gpu/drm/clients/drm_fbdev_client.c +4 -10
@@ -62,26 +62,20 @@ err_drm_err:
 	return ret;
 }
 
-static int drm_fbdev_client_suspend(struct drm_client_dev *client, bool holds_console_lock)
+static int drm_fbdev_client_suspend(struct drm_client_dev *client)
 {
 	struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client);
 
-	if (holds_console_lock)
-		drm_fb_helper_set_suspend(fb_helper, true);
-	else
-		drm_fb_helper_set_suspend_unlocked(fb_helper, true);
+	drm_fb_helper_set_suspend_unlocked(fb_helper, true);
 
 	return 0;
 }
 
-static int drm_fbdev_client_resume(struct drm_client_dev *client, bool holds_console_lock)
+static int drm_fbdev_client_resume(struct drm_client_dev *client)
 {
 	struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client);
 
-	if (holds_console_lock)
-		drm_fb_helper_set_suspend(fb_helper, false);
-	else
-		drm_fb_helper_set_suspend_unlocked(fb_helper, false);
+	drm_fb_helper_set_suspend_unlocked(fb_helper, false);
 
 	return 0;
 }
drivers/gpu/drm/clients/drm_log.c +2 -2
@@ -328,7 +328,7 @@ static int drm_log_client_hotplug(struct
 	return 0;
 }
 
-static int drm_log_client_suspend(struct drm_client_dev *client, bool _console_lock)
+static int drm_log_client_suspend(struct drm_client_dev *client)
 {
 	struct drm_log *dlog = client_to_drm_log(client);
 
@@ -337,7 +337,7 @@ static int drm_log_client_suspend(struct
 	return 0;
 }
 
-static int drm_log_client_resume(struct drm_client_dev *client, bool _console_lock)
+static int drm_log_client_resume(struct drm_client_dev *client)
 {
 	struct drm_log *dlog = client_to_drm_log(client);
 
drivers/gpu/drm/drm_client_event.c +8 -8
@@ -122,7 +122,7 @@ void drm_client_dev_restore(struct drm_d
 	mutex_unlock(&dev->clientlist_mutex);
 }
 
-static int drm_client_suspend(struct drm_client_dev *client, bool holds_console_lock)
+static int drm_client_suspend(struct drm_client_dev *client)
 {
 	struct drm_device *dev = client->dev;
 	int ret = 0;
@@ -131,7 +131,7 @@ static int drm_client_suspend(struct drm
 		return 0;
 
 	if (client->funcs && client->funcs->suspend)
-		ret = client->funcs->suspend(client, holds_console_lock);
+		ret = client->funcs->suspend(client);
 	drm_dbg_kms(dev, "%s: ret=%d\n", client->name, ret);
 
 	client->suspended = true;
@@ -139,20 +139,20 @@ static int drm_client_suspend(struct drm
 	return ret;
 }
 
-void drm_client_dev_suspend(struct drm_device *dev, bool holds_console_lock)
+void drm_client_dev_suspend(struct drm_device *dev)
 {
 	struct drm_client_dev *client;
 
 	mutex_lock(&dev->clientlist_mutex);
 	list_for_each_entry(client, &dev->clientlist, list) {
 		if (!client->suspended)
-			drm_client_suspend(client, holds_console_lock);
+			drm_client_suspend(client);
 	}
 	mutex_unlock(&dev->clientlist_mutex);
 }
 EXPORT_SYMBOL(drm_client_dev_suspend);
 
-static int drm_client_resume(struct drm_client_dev *client, bool holds_console_lock)
+static int drm_client_resume(struct drm_client_dev *client)
 {
 	struct drm_device *dev = client->dev;
 	int ret = 0;
@@ -161,7 +161,7 @@ static int drm_client_resume(struct drm_
 		return 0;
 
 	if (client->funcs && client->funcs->resume)
-		ret = client->funcs->resume(client, holds_console_lock);
+		ret = client->funcs->resume(client);
 	drm_dbg_kms(dev, "%s: ret=%d\n", client->name, ret);
 
 	client->suspended = false;
@@ -172,14 +172,14 @@ static int drm_client_resume(struct drm_
 	return ret;
 }
 
-void drm_client_dev_resume(struct drm_device *dev, bool holds_console_lock)
+void drm_client_dev_resume(struct drm_device *dev)
 {
 	struct drm_client_dev *client;
 
 	mutex_lock(&dev->clientlist_mutex);
 	list_for_each_entry(client, &dev->clientlist, list) {
 		if  (client->suspended)
-			drm_client_resume(client, holds_console_lock);
+			drm_client_resume(client);
 	}
 	mutex_unlock(&dev->clientlist_mutex);
 }
drivers/gpu/drm/drm_modeset_helper.c +3 -3
@@ -203,10 +203,10 @@ int drm_mode_config_helper_suspend(struc
 	if (dev->mode_config.poll_enabled)
 		drm_kms_helper_poll_disable(dev);
 
-	drm_client_dev_suspend(dev, false);
+	drm_client_dev_suspend(dev);
 	state = drm_atomic_helper_suspend(dev);
 	if (IS_ERR(state)) {
-		drm_client_dev_resume(dev, false);
+		drm_client_dev_resume(dev);
 
 		/*
 		 * Don't enable polling if it was never initialized
@@ -252,7 +252,7 @@ int drm_mode_config_helper_resume(struct
 		DRM_ERROR("Failed to resume (%d)\n", ret);
 	dev->mode_config.suspend_state = NULL;
 
-	drm_client_dev_resume(dev, false);
+	drm_client_dev_resume(dev);
 
 	/*
 	 * Don't enable polling if it is not initialized
drivers/gpu/drm/i915/i915_driver.c +3 -3
@@ -978,7 +978,7 @@ void i915_driver_shutdown(struct drm_i91
 	intel_runtime_pm_disable(&i915->runtime_pm);
 	intel_power_domains_disable(display);
 
-	drm_client_dev_suspend(&i915->drm, false);
+	drm_client_dev_suspend(&i915->drm);
 	if (intel_display_device_present(display)) {
 		drm_kms_helper_poll_disable(&i915->drm);
 		intel_display_driver_disable_user_access(display);
@@ -1061,7 +1061,7 @@ static int i915_drm_suspend(struct drm_d
 	/* We do a lot of poking in a lot of registers, make sure they work
 	 * properly. */
 	intel_power_domains_disable(display);
-	drm_client_dev_suspend(dev, false);
+	drm_client_dev_suspend(dev);
 	if (intel_display_device_present(display)) {
 		drm_kms_helper_poll_disable(dev);
 		intel_display_driver_disable_user_access(display);
@@ -1245,7 +1245,7 @@ static int i915_drm_resume(struct drm_de
 
 	intel_opregion_resume(display);
 
-	drm_client_dev_resume(dev, false);
+	drm_client_dev_resume(dev);
 
 	intel_power_domains_enable(display);
 
drivers/gpu/drm/nouveau/nouveau_display.c +2 -2
@@ -764,7 +764,7 @@ nouveau_display_suspend(struct drm_devic
 {
 	struct nouveau_display *disp = nouveau_display(dev);
 
-	drm_client_dev_suspend(dev, false);
+	drm_client_dev_suspend(dev);
 
 	if (drm_drv_uses_atomic_modeset(dev)) {
 		if (!runtime) {
@@ -795,7 +795,7 @@ nouveau_display_resume(struct drm_device
 		}
 	}
 
-	drm_client_dev_resume(dev, false);
+	drm_client_dev_resume(dev);
 }
 
 int
drivers/gpu/drm/radeon/radeon_device.c +2 -2
@@ -1635,7 +1635,7 @@ int radeon_suspend_kms(struct drm_device
 	}
 
 	if (notify_clients)
-		drm_client_dev_suspend(dev, false);
+		drm_client_dev_suspend(dev);
 
 	return 0;
 }
@@ -1739,7 +1739,7 @@ int radeon_resume_kms(struct drm_device
 		radeon_pm_compute_clocks(rdev);
 
 	if (notify_clients)
-		drm_client_dev_resume(dev, false);
+		drm_client_dev_resume(dev);
 
 	return 0;
 }
drivers/gpu/drm/xe/display/xe_display.c +3 -3
@@ -331,7 +331,7 @@ void xe_display_pm_suspend(struct xe_dev
 	 * properly.
 	 */
 	intel_power_domains_disable(display);
-	drm_client_dev_suspend(&xe->drm, false);
+	drm_client_dev_suspend(&xe->drm);
 
 	if (intel_display_device_present(display)) {
 		drm_kms_helper_poll_disable(&xe->drm);
@@ -363,7 +363,7 @@ void xe_display_pm_shutdown(struct xe_de
 		return;
 
 	intel_power_domains_disable(display);
-	drm_client_dev_suspend(&xe->drm, false);
+	drm_client_dev_suspend(&xe->drm);
 
 	if (intel_display_device_present(display)) {
 		drm_kms_helper_poll_disable(&xe->drm);
@@ -488,7 +488,7 @@ void xe_display_pm_resume(struct xe_devi
 
 	intel_opregion_resume(display);
 
-	drm_client_dev_resume(&xe->drm, false);
+	drm_client_dev_resume(&xe->drm);
 
 	intel_power_domains_enable(display);
 }
include/drm/drm_client.h +2 -12
@@ -70,13 +70,8 @@ struct drm_client_funcs {
 	 * Called when suspending the device.
 	 *
 	 * This callback is optional.
-	 *
-	 * FIXME: Some callers hold the console lock when invoking this
-	 *        function. This interferes with fbdev emulation, which
-	 *        also tries to acquire the lock. Push the console lock
-	 *        into the callback and remove 'holds_console_lock'.
 	 */
-	int (*suspend)(struct drm_client_dev *client, bool holds_console_lock);
+	int (*suspend)(struct drm_client_dev *client);
 
 	/**
 	 * @resume:
@@ -84,13 +79,8 @@ struct drm_client_funcs {
 	 * Called when resuming the device from suspend.
 	 *
 	 * This callback is optional.
-	 *
-	 * FIXME: Some callers hold the console lock when invoking this
-	 *        function. This interferes with fbdev emulation, which
-	 *        also tries to acquire the lock. Push the console lock
-	 *        into the callback and remove 'holds_console_lock'.
 	 */
-	int (*resume)(struct drm_client_dev *client, bool holds_console_lock);
+	int (*resume)(struct drm_client_dev *client);
 };
 
 /**
include/drm/drm_client_event.h +4 -4
@@ -11,8 +11,8 @@ struct drm_device;
 void drm_client_dev_unregister(struct drm_device *dev);
 void drm_client_dev_hotplug(struct drm_device *dev);
 void drm_client_dev_restore(struct drm_device *dev);
-void drm_client_dev_suspend(struct drm_device *dev, bool holds_console_lock);
-void drm_client_dev_resume(struct drm_device *dev, bool holds_console_lock);
+void drm_client_dev_suspend(struct drm_device *dev);
+void drm_client_dev_resume(struct drm_device *dev);
 #else
 static inline void drm_client_dev_unregister(struct drm_device *dev)
 { }
@@ -20,9 +20,9 @@ static inline void drm_client_dev_hotplu
 { }
 static inline void drm_client_dev_restore(struct drm_device *dev)
 { }
-static inline void drm_client_dev_suspend(struct drm_device *dev, bool holds_console_lock)
+static inline void drm_client_dev_suspend(struct drm_device *dev)
 { }
-static inline void drm_client_dev_resume(struct drm_device *dev, bool holds_console_lock)
+static inline void drm_client_dev_resume(struct drm_device *dev)
 { }
 #endif
 
author maintainer
#360 lore raw

PATCH 360/395 drm/client: Pass force parameter to client restore

In reply to Greg Kroah-Hartman

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

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

From: Thomas Zimmermann <[email protected]>

[ Upstream commit 943240d342f148896733eb6c7b223a08aa1f520a ]

Add force parameter to client restore and pass value through the layers. The only currently used value is false.

If force is true, the client should restore its display even if it does not hold the DRM master lock. This is be required for emergency output, such as sysrq.

While at it, inline drm_fb_helper_lastclose(), which is a trivial wrapper around drm_fb_helper_restore_fbdev_mode_unlocked().

Stable-dep-of: 1fca688e9443 ("drm/client: fix restore of partially initialized client")

---
 drivers/gpu/drm/clients/drm_fbdev_client.c |    6 ++++--
 drivers/gpu/drm/drm_client_event.c         |    4 ++--
 drivers/gpu/drm/drm_fb_helper.c            |   24 ++++++------------------
 drivers/gpu/drm/drm_file.c                 |    2 +-
 include/drm/drm_client.h                   |    8 +++++---
 include/drm/drm_client_event.h             |    4 ++--
 include/drm/drm_fb_helper.h                |    8 ++------
 7 files changed, 22 insertions(+), 34 deletions(-)
drivers/gpu/drm/clients/drm_fbdev_client.c +4 -2
@@ -26,9 +26,11 @@ static void drm_fbdev_client_unregister(
 	}
 }
 
-static int drm_fbdev_client_restore(struct drm_client_dev *client)
+static int drm_fbdev_client_restore(struct drm_client_dev *client, bool force)
 {
-	drm_fb_helper_lastclose(client->dev);
+	struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client);
+
+	drm_fb_helper_restore_fbdev_mode_unlocked(fb_helper, force);
 
 	return 0;
 }
drivers/gpu/drm/drm_client_event.c +2 -2
@@ -101,7 +101,7 @@ void drm_client_dev_hotplug(struct drm_d
 }
 EXPORT_SYMBOL(drm_client_dev_hotplug);
 
-void drm_client_dev_restore(struct drm_device *dev)
+void drm_client_dev_restore(struct drm_device *dev, bool force)
 {
 	struct drm_client_dev *client;
 	int ret;
@@ -114,7 +114,7 @@ void drm_client_dev_restore(struct drm_d
 		if (!client->funcs || !client->funcs->restore)
 			continue;
 
-		ret = client->funcs->restore(client);
+		ret = client->funcs->restore(client, force);
 		drm_dbg_kms(dev, "%s: ret=%d\n", client->name, ret);
 		if (!ret) /* The first one to return zero gets the privilege to restore */
 			break;
drivers/gpu/drm/drm_fb_helper.c +6 -18
@@ -253,6 +253,7 @@ __drm_fb_helper_restore_fbdev_mode_unloc
 /**
  * drm_fb_helper_restore_fbdev_mode_unlocked - restore fbdev configuration
  * @fb_helper: driver-allocated fbdev helper, can be NULL
+ * @force: ignore present DRM master
  *
  * This helper should be called from fbdev emulation's &drm_client_funcs.restore
  * callback. It ensures that the user isn't greeted with a black screen when the
@@ -261,9 +262,9 @@ __drm_fb_helper_restore_fbdev_mode_unloc
  * Returns:
  * 0 on success, or a negative errno code otherwise.
  */
-int drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper)
+int drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper, bool force)
 {
-	return __drm_fb_helper_restore_fbdev_mode_unlocked(fb_helper, false);
+	return __drm_fb_helper_restore_fbdev_mode_unlocked(fb_helper, force);
 }
 EXPORT_SYMBOL(drm_fb_helper_restore_fbdev_mode_unlocked);
 
@@ -1316,9 +1317,9 @@ int drm_fb_helper_set_par(struct fb_info
 	 * the KDSET IOCTL with KD_TEXT, and only after that drops the master
 	 * status when exiting.
 	 *
-	 * In the past this was caught by drm_fb_helper_lastclose(), but on
-	 * modern systems where logind always keeps a drm fd open to orchestrate
-	 * the vt switching, this doesn't work.
+	 * In the past this was caught by drm_fb_helper_restore_fbdev_mode_unlocked(),
+	 * but on modern systems where logind always keeps a drm fd open to
+	 * orchestrate the vt switching, this doesn't work.
 	 *
 	 * To not break the userspace ABI we have this special case here, which
 	 * is only used for the above case. Everything else uses the normal
@@ -1946,16 +1947,3 @@ int drm_fb_helper_hotplug_event(struct d
 	return 0;
 }
 EXPORT_SYMBOL(drm_fb_helper_hotplug_event);
-
-/**
- * drm_fb_helper_lastclose - DRM driver lastclose helper for fbdev emulation
- * @dev: DRM device
- *
- * This function is obsolete. Call drm_fb_helper_restore_fbdev_mode_unlocked()
- * instead.
- */
-void drm_fb_helper_lastclose(struct drm_device *dev)
-{
-	drm_fb_helper_restore_fbdev_mode_unlocked(dev->fb_helper);
-}
-EXPORT_SYMBOL(drm_fb_helper_lastclose);
drivers/gpu/drm/drm_file.c +1 -1
@@ -405,7 +405,7 @@ EXPORT_SYMBOL(drm_open);
 
 static void drm_lastclose(struct drm_device *dev)
 {
-	drm_client_dev_restore(dev);
+	drm_client_dev_restore(dev, false);
 
 	if (dev_is_pci(dev->dev))
 		vga_switcheroo_process_delayed_switch();
include/drm/drm_client.h +5 -3
@@ -47,12 +47,14 @@ struct drm_client_funcs {
 	 *
 	 * Note that the core does not guarantee exclusion against concurrent
 	 * drm_open(). Clients need to ensure this themselves, for example by
-	 * using drm_master_internal_acquire() and
-	 * drm_master_internal_release().
+	 * using drm_master_internal_acquire() and drm_master_internal_release().
+	 *
+	 * If the caller passes force, the client should ignore any present DRM
+	 * master and restore the display anyway.
 	 *
 	 * This callback is optional.
 	 */
-	int (*restore)(struct drm_client_dev *client);
+	int (*restore)(struct drm_client_dev *client, bool force);
 
 	/**
 	 * @hotplug:
include/drm/drm_client_event.h +2 -2
@@ -10,7 +10,7 @@ struct drm_device;
 #if defined(CONFIG_DRM_CLIENT)
 void drm_client_dev_unregister(struct drm_device *dev);
 void drm_client_dev_hotplug(struct drm_device *dev);
-void drm_client_dev_restore(struct drm_device *dev);
+void drm_client_dev_restore(struct drm_device *dev, bool force);
 void drm_client_dev_suspend(struct drm_device *dev);
 void drm_client_dev_resume(struct drm_device *dev);
 #else
@@ -18,7 +18,7 @@ static inline void drm_client_dev_unregi
 { }
 static inline void drm_client_dev_hotplug(struct drm_device *dev)
 { }
-static inline void drm_client_dev_restore(struct drm_device *dev)
+static inline void drm_client_dev_restore(struct drm_device *dev, bool force)
 { }
 static inline void drm_client_dev_suspend(struct drm_device *dev)
 { }
include/drm/drm_fb_helper.h +2 -6
@@ -254,7 +254,8 @@ int drm_fb_helper_set_par(struct fb_info
 int drm_fb_helper_check_var(struct fb_var_screeninfo *var,
 			    struct fb_info *info);
 
-int drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper);
+int drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper,
+					      bool force);
 
 void drm_fb_helper_unregister_info(struct drm_fb_helper *fb_helper);
 void drm_fb_helper_fill_info(struct fb_info *info,
@@ -281,7 +282,6 @@ int drm_fb_helper_hotplug_event(struct d
 int drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper);
 int drm_fb_helper_debug_enter(struct fb_info *info);
 int drm_fb_helper_debug_leave(struct fb_info *info);
-void drm_fb_helper_lastclose(struct drm_device *dev);
 #else
 static inline void drm_fb_helper_prepare(struct drm_device *dev,
 					 struct drm_fb_helper *helper,
@@ -397,10 +397,6 @@ static inline int drm_fb_helper_debug_le
 {
 	return 0;
 }
-
-static inline void drm_fb_helper_lastclose(struct drm_device *dev)
-{
-}
 #endif
 
 #endif
author maintainer
#361 lore raw

PATCH 361/395 drm/client: fix restore of partially initialized client

In reply to Greg Kroah-Hartman

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

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

From: shechenglong <[email protected]>

[ Upstream commit 1fca688e9443003e33cf30453e7a7560367656c9 ]

I got a null-ptr-deref report when closing a DRM file descriptor:

WARNING: drivers/gpu/drm/drm_atomic.c:2031 at
__drm_atomic_helper_set_config+0x18e/0x1b0 [drm]

Call Trace:
drm_client_modeset_commit_atomic+0x16b/0x220 [drm]
drm_client_modeset_commit_locked+0x56/0x160 [drm]
drm_client_modeset_commit+0x21/0x40 [drm]
__drm_fb_helper_restore_fbdev_mode_unlocked.part.0+0x7b/0x80
drm_fbdev_client_restore+0xe/0x20 [drm_client_lib]
drm_client_dev_restore+0x9f/0xc0 [drm]
drm_release+0xc5/0xe0 [drm]

The warning is followed by a NULL pointer dereference:

BUG: kernel NULL pointer dereference, address: 0000000000000008

RIP:
__drm_fb_helper_restore_fbdev_mode_unlocked.part.0+0x41/0x80 [drm_kms_helper]

Call Trace:
drm_fbdev_client_restore+0xe/0x20 [drm_client_lib]
drm_client_dev_restore+0x9f/0xc0 [drm]
drm_release+0xc5/0xe0 [drm]
__fput+0xdc/0x2b0
__x64_sys_close+0x39/0x80
do_syscall_64+0x8d/0x460
entry_SYSCALL_64_after_hwframe+0x76/0x7e

drm_client_register() adds the DRM client to the device client list before invoking the initial hotplug callback. If the hotplug callback fails, the client remains registered.

For the fbdev client, a failure during drm_fb_helper_initial_config() causes the partially initialized fbdev helper to be cleaned up. drm_fb_helper_fini() releases fb_helper->info and leaves it NULL.

The fbdev client therefore remains registered even though there is no fully initialized framebuffer device.

Later, when userspace closes the DRM file descriptor, drm_release() can invoke the restore callbacks of registered DRM clients:

drm_release()
drm_client_dev_restore()
drm_fbdev_client_restore()
drm_fb_helper_restore_fbdev_mode_unlocked()

drm_fbdev_client_restore() currently restores the fbdev state unconditionally. For a partially initialized fbdev client this can submit an incomplete modeset state and subsequently access fbdev state which has not been initialized, resulting in the warning and NULL pointer dereference above.

drm_fbdev_client_unregister() already uses fb_helper->info to distinguish a fully probed framebuffer device from a partially initialized client.

Use the same condition in drm_fbdev_client_restore() and skip restore if no framebuffer device has been successfully initialized.

---
 drivers/gpu/drm/clients/drm_fbdev_client.c |    8 ++++++++
 1 file changed, 8 insertions(+)
drivers/gpu/drm/clients/drm_fbdev_client.c +8 -0
@@ -30,6 +30,14 @@ static int drm_fbdev_client_restore(stru
 {
 	struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client);
 
+	/*
+	 * The client is registered before the initial fbdev probe.
+	 * If probing failed, the client remains registered but there
+	 * is no valid fbdev framebuffer to restore.
+	 */
+	if (!fb_helper->info || !fb_helper->fb)
+		return 0;
+
 	drm_fb_helper_restore_fbdev_mode_unlocked(fb_helper, force);
 
 	return 0;
author maintainer
#362 lore raw

PATCH 362/395 mm/rmap: allocate anon_vma_chain objects unlocked when possible

In reply to Greg Kroah-Hartman

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

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

From: Lorenzo Stoakes <[email protected]>

[ Upstream commit bfc2b13b05a1343bb60a85d840fd8956731866c5 ]

There is no reason to allocate the anon_vma_chain under the anon_vma write lock when cloning - we can in fact assign these to the destination VMA safely as we hold the exclusive mmap lock and therefore preclude anybody else accessing these fields.

We only need take the anon_vma write lock when we link rbtree edges from the anon_vma to the newly established AVCs.

This also allows us to eliminate the weird GFP_NOWAIT, GFP_KERNEL dance introduced in commit dd34739c03f2 ("mm: avoid anon_vma_chain allocation under anon_vma lock"), further simplifying this logic.

This should reduce lock anon_vma contention, and clarifies exactly where the anon_vma lock is required.

We cannot adjust __anon_vma_prepare() in the same way as this is only protected by VMA read lock, so we have to perform the allocation here under the anon_vma write lock and page_table_lock (to protect against racing threads), and we wish to retain the lock ordering.

With this change we can simplify cleanup_partial_anon_vmas() even further
- since we allocate AVC's without any lock taken and do not insert anything into the interval tree until after the allocations are tried, we can remove all logic pertaining to this and just free up AVC's only.

[Stable dependency adaptation for 6.18:
Keep the split of anon_vma_chain_link() into anon_vma_chain_assign() and explicit interval-tree insertion at all three callers. Retain the fork change that assigns the chain before taking the anon_vma write lock; the interval-tree insertion remains protected by that lock.

Drop the two-pass clone allocation and cleanup_partial_anon_vmas() changes. This tree lacks the prerequisite clone assertions, early unfaulted-VMA handling, simplified root locking, and partial-clone cleanup. Preserve the existing GFP_NOWAIT/GFP_KERNEL fallback, root locking, list traversal, reference accounting, and allocation-failure cleanup instead. No new functions are introduced.

The helper split supplies the context needed for b6ac0b3f6013 ("mm/rmap:
fix missing barrier between anon_vma init and vma->anon_vma publish") to merge cleanly while retaining this tree's interval-tree API. The memory barrier fix itself is left to that target commit.]

Stable-dep-of: b6ac0b3f6013 ("mm/rmap: fix missing barrier between anon_vma init and vma->anon_vma publish")

---
 mm/rmap.c |   17 ++++++++++-------
 1 file changed, 10 insertions(+), 7 deletions(-)
mm/rmap.c +10 -7
@@ -146,14 +146,13 @@ static void anon_vma_chain_free(struct a
 	kmem_cache_free(anon_vma_chain_cachep, anon_vma_chain);
 }
 
-static void anon_vma_chain_link(struct vm_area_struct *vma,
-				struct anon_vma_chain *avc,
-				struct anon_vma *anon_vma)
+static void anon_vma_chain_assign(struct vm_area_struct *vma,
+				  struct anon_vma_chain *avc,
+				  struct anon_vma *anon_vma)
 {
 	avc->vma = vma;
 	avc->anon_vma = anon_vma;
 	list_add(&avc->same_vma, &vma->anon_vma_chain);
-	anon_vma_interval_tree_insert(avc, &anon_vma->rb_root);
 }
 
 /**
@@ -210,7 +209,8 @@ int __anon_vma_prepare(struct vm_area_st
 	spin_lock(&mm->page_table_lock);
 	if (likely(!vma->anon_vma)) {
 		vma->anon_vma = anon_vma;
-		anon_vma_chain_link(vma, avc, anon_vma);
+		anon_vma_chain_assign(vma, avc, anon_vma);
+		anon_vma_interval_tree_insert(avc, &anon_vma->rb_root);
 		anon_vma->num_active_vmas++;
 		allocated = NULL;
 		avc = NULL;
@@ -294,7 +294,8 @@ int anon_vma_clone(struct vm_area_struct
 		}
 		anon_vma = pavc->anon_vma;
 		root = lock_anon_vma_root(root, anon_vma);
-		anon_vma_chain_link(dst, avc, anon_vma);
+		anon_vma_chain_assign(dst, avc, anon_vma);
+		anon_vma_interval_tree_insert(avc, &anon_vma->rb_root);
 
 		/*
 		 * Reuse existing anon_vma if it has no vma and only one
@@ -378,8 +379,10 @@ int anon_vma_fork(struct vm_area_struct
 	get_anon_vma(anon_vma->root);
 	/* Mark this anon_vma as the one where our new (COWed) pages go. */
 	vma->anon_vma = anon_vma;
+	anon_vma_chain_assign(vma, avc, anon_vma);
+	/* Now let rmap see it. */
 	anon_vma_lock_write(anon_vma);
-	anon_vma_chain_link(vma, avc, anon_vma);
+	anon_vma_interval_tree_insert(avc, &anon_vma->rb_root);
 	anon_vma->parent->num_children++;
 	anon_vma_unlock_write(anon_vma);
 
author maintainer
#363 lore raw

PATCH 363/395 mm/rmap: fix missing barrier between anon_vma init and vma->anon_vma publish

In reply to Greg Kroah-Hartman

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

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

From: Jinjiang Tu <[email protected]>

[ Upstream commit b6ac0b3f6013c168f22cad97e79967accacb08e1 ]

On arm64 server, we find that a task trying to grab the anon_vma lock triggers hungtask.

INFO: task main:2354726 blocked for more than 120 seconds.
      Tainted: G            E     5.10.0-0021.aarch64 #1
"echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
task:main            state:D stack:    0 pid:2354726 ppid:2350673 flags:0x00000a01
Call trace:
 __switch_to+0x7c/0xbc
 __schedule+0x3b4/0x8a0
 schedule+0x50/0xe0
 rwsem_down_write_slowpath+0x3cc/0x6cc
 down_write+0x60/0x260
 __anon_vma_prepare+0x6c/0x210
 do_anonymous_page+0x258/0x660
 handle_pte_fault+0x188/0x214
 __handle_mm_fault+0x1b0/0x380
 handle_mm_fault+0xf4/0x284
 do_page_fault+0x19c/0x494
 do_translation_fault+0xcc/0xf8
 do_mem_abort+0x48/0xac
 el0_da+0x44/0x80
 el0_sync_handler+0x88/0xb4
 el0_sync+0x160/0x180

After analyzing the vmcore, we found the anon_vma->root->rwsem.count is -1. There is another anon_vma whose anon_vma->root->rwsem.count is 1, the anon_vma->root->rwsem.owner shows the lock is held, but the stack of the task shows the task doesn't hold the anon_vma lock.

After adding more debugging info, we found __anon_vma_prepare() reuses anon_vma and triggers the UAF of anon_vma->root due to missing memory barrier, leading to locking and unlocking two different anon_vma->root, thus leading to an anon_vma will never be unlocked, and another anon_vma couldn't be locked anymore.

This race requires two adjacent VMAs that are not merged but are anon_vma-compatible (e.g., they differ in VMA_ACCESS_FLAGS that can be changed by mprotect()). Two threads fault on each VMA concurrently, both calling __anon_vma_prepare() with only mmap_lock held for reading.

THREAD A THREAD B
__anon_vma_prepare __anon_vma_prepare
find_mergeable_anon_vma() -> NULL
anon_vma = anon_vma_alloc();
anon_vma->root = anon_vma;
// the two stores may be reordered

 vma->anon_vma = anon_vma;
                                   // finds A's anon_vma
                                   anon_vma = find_mergeable_anon_vma(vma);
                                   anon_vma_lock_write(anon_vma);
                                     // may still see the old root
                                     down_write(&anon_vma->root->rwsem);
                                   anon_vma_unlock_write(anon_vma);
                                     // see the new root, never unlock old
                                     up_write(&anon_vma->root->rwsem);

thread A triggers page fault and calls __anon_vma_prepare() to prepare anon_vma for the faulting vma. __anon_vma_prepare() allocates and initializes a new anon_vma, and then publishes it to the vma with a plain store. anon_vma_prepare() only requires the mmap_lock to be held for reading, so two threads can fault on adjacent VMAs at the same time. While thread A publishes a new anon_vma, thread B could find the anon_vma via find_mergeable_anon_vma() and then locks anon_vma->root->rwsem.

The store to anon_vma->root in anon_vma_alloc() and the store to vma->anon_vma can be reordered. The anon_vma_lock_write() and spin_lock() only provide acquire semantics, which do not prevent prior stores from being reordered after them. The release semantics of the corresponding spin_unlock() and anon_vma_unlock_write() come too late, the store to vma->anon_vma is already published before they take effect. As a result, thread B can observe the following order:

    vma->anon_vma = anon_vma;
    anon_vma->root = anon_vma;

The anon_vma slab is SLAB_TYPESAFE_BY_RCU, so a newly allocated anon_vma may reuse memory from a previously freed one. The constructor (anon_vma_ctor) does not reset anon_vma->root, and __put_anon_vma() doesn't clear it either, so the old root value persists until anon_vma_alloc() overwrites it. If that store isn't visible, thread B reads a root that points to the old anon_vma and locks it.

As a result, thread B can call anon_vma_lock_write() with the old root, and call anon_vma_unlock_write() with the new root, leading to an anon_vma will never be unlocked, and another anon_vma couldn't be locked anymore (its count is dropped from 0 to -1 due to wrong unlock).

To fix it, change the plain store vma->anon_vma = anon_vma to store release, so that the fields of anon_vma are visible before anon_vma is published to vma->anon_vma.

At read side, the load of anon_vma and anon_vma->root have address dependency. According to Documentation/memory-barriers.txt and some investigations, only Alpha needs address-dependency barriers and it has been handled by READ_ONCE() in reusable_anon_vma().

We reproduced this issue in v5.10 with KSM enabled. The kernel doesn't merge commit cf7e7a3503df ("mm: prevent KSM from breaking VMA merging for new VMAs"), so there are many adjacent VMAs that aren't merged but are compatible for anon_vma.

Without this fix, our production environment could reproduce this issue about 2-5 times each month. After adding a smp_mb() before anon_vma_lock_write(anon_vma) in __anon_vma_prepare(), which is different to this patch, this issue hasn't been reproduced for one month.

---
 mm/rmap.c |    6 +++++-
 mm/vma.c  |    8 ++++++++
 2 files changed, 13 insertions(+), 1 deletion(-)
mm/rmap.c +5 -1
@@ -208,7 +208,11 @@ int __anon_vma_prepare(struct vm_area_st
 	/* page_table_lock to protect against threads */
 	spin_lock(&mm->page_table_lock);
 	if (likely(!vma->anon_vma)) {
-		vma->anon_vma = anon_vma;
+		/*
+		 * Make anon_vma fields visible before anon_vma is published.
+		 * Paired with an address dependency in reusable_anon_vma().
+		 */
+		smp_store_release(&vma->anon_vma, anon_vma);
 		anon_vma_chain_assign(vma, avc, anon_vma);
 		anon_vma_interval_tree_insert(avc, &anon_vma->rb_root);
 		anon_vma->num_active_vmas++;
mm/vma.c +8 -0
@@ -1979,6 +1979,13 @@ static int anon_vma_compatible(struct vm
  * acceptable for merging, so we can do all of this optimistically. But
  * we do that READ_ONCE() to make sure that we never re-load the pointer.
  *
+ * The READ_ONCE() establishes an address dependency between anon_vma and
+ * any access to its fields, which pairs with the assignment to
+ * vma->anon_vma performed with release semantics in __anon_vma_prepare().
+ *
+ * This is especially important as anon_vma's are SLAB_TYPESAFE_BY_RCU so
+ * accessing an uninitialised anon_vma's fields may result in a UAF.
+ *
  * IOW: that the "list_is_singular()" test on the anon_vma_chain only
  * matters for the 'stable anon_vma' case (ie the thing we want to avoid
  * is to return an anon_vma that is "complex" due to having gone through
@@ -1993,6 +2000,7 @@ static struct anon_vma *reusable_anon_vm
 					  struct vm_area_struct *b)
 {
 	if (anon_vma_compatible(a, b)) {
+		/* Paired with a memory barrier in __anon_vma_prepare(). */
 		struct anon_vma *anon_vma = READ_ONCE(old->anon_vma);
 
 		if (anon_vma && list_is_singular(&old->anon_vma_chain))
author maintainer
#364 lore raw

PATCH 364/395 mm/hugetlb: create hstate_is_gigantic_no_runtime helper

In reply to Greg Kroah-Hartman

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

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

From: Usama Arif <[email protected]>

[ Upstream commit a743e0af503a633e4ca68a100d9b2a1a071fe8ae ]

This is a common condition used to skip operations that cannot be performed on gigantic pages when runtime support is disabled. This helper is introduced as the condition will exist even more when allowing "overcommit" of gigantic hugepages. No functional change intended with this patch.

Stable-dep-of: a363c62a653c ("mm/hugetlb: do not dissolve gigantic pages without runtime support")

---
 mm/hugetlb.c |   21 ++++++++++++++++-----
 1 file changed, 16 insertions(+), 5 deletions(-)
mm/hugetlb.c +16 -5
@@ -138,6 +138,17 @@ static void hugetlb_free_folio(struct fo
 	folio_put(folio);
 }
 
+/*
+ * Check if the hstate represents gigantic pages but gigantic page
+ * runtime support is not available. This is a common condition used to
+ * skip operations that cannot be performed on gigantic pages when runtime
+ * support is disabled.
+ */
+static inline bool hstate_is_gigantic_no_runtime(struct hstate *h)
+{
+	return hstate_is_gigantic(h) && !gigantic_page_runtime_supported();
+}
+
 static inline bool subpool_is_free(struct hugepage_subpool *spool)
 {
 	if (spool->count)
@@ -1538,7 +1549,7 @@ static void remove_hugetlb_folio(struct
 	VM_BUG_ON_FOLIO(hugetlb_cgroup_from_folio_rsvd(folio), folio);
 
 	lockdep_assert_held(&hugetlb_lock);
-	if (hstate_is_gigantic(h) && !gigantic_page_runtime_supported())
+	if (hstate_is_gigantic_no_runtime(h))
 		return;
 
 	list_del(&folio->lru);
@@ -1600,7 +1611,7 @@ static void __update_and_free_hugetlb_fo
 {
 	bool clear_flag = folio_test_hugetlb_vmemmap_optimized(folio);
 
-	if (hstate_is_gigantic(h) && !gigantic_page_runtime_supported())
+	if (hstate_is_gigantic_no_runtime(h))
 		return;
 
 	/*
@@ -2496,7 +2507,7 @@ static void return_unused_surplus_pages(
 	/* Uncommit the reservation */
 	h->resv_huge_pages -= unused_resv_pages;
 
-	if (hstate_is_gigantic(h) && !gigantic_page_runtime_supported())
+	if (hstate_is_gigantic_no_runtime(h))
 		goto out;
 
 	/*
@@ -3711,7 +3722,7 @@ static void __init hugetlb_init_hstates(
 		 * - If CMA allocation is possible, we can not demote
 		 *   HUGETLB_PAGE_ORDER or smaller size pages.
 		 */
-		if (hstate_is_gigantic(h) && !gigantic_page_runtime_supported())
+		if (hstate_is_gigantic_no_runtime(h))
 			continue;
 		if (hugetlb_cma_total_size() && h->order <= HUGETLB_PAGE_ORDER)
 			continue;
@@ -4188,7 +4199,7 @@ static ssize_t __nr_hugepages_store_comm
 	int err;
 	nodemask_t nodes_allowed, *n_mask;
 
-	if (hstate_is_gigantic(h) && !gigantic_page_runtime_supported())
+	if (hstate_is_gigantic_no_runtime(h))
 		return -EINVAL;
 
 	if (nid == NUMA_NO_NODE) {
author maintainer
#365 lore raw

PATCH 365/395 mm/hugetlb: do not dissolve gigantic pages without runtime support

In reply to Greg Kroah-Hartman

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

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

From: Longlong Xia <[email protected]>

[ Upstream commit a363c62a653cc8b3e21da9545fa4e028ef50f9c3 ]

dissolve_free_hugetlb_folio() doesn't check hstate_is_gigantic_no_runtime(h) though remove_hugetlb_folio()/ update_and_free_hugetlb_folio() silently bail for such folios, so it frees a still-listed folio and, on vmemmap restore failure, the add_hugetlb_folio() rollback corrupts the free list.

Fixes 6eb4e88a6d27 ("hugetlb: create remove_hugetlb_page() to separate functionality")
Assisted-by Codex:gpt-5.6-sol
Cc David Hildenbrand <[email protected]>
Cc Miaohe Lin <[email protected]>
Cc Michal Hocko <[email protected]>
Cc Oscar Salvador <[email protected]>
---
 mm/hugetlb.c |    9 +++++++++
 1 file changed, 9 insertions(+)
mm/hugetlb.c +9 -0
@@ -2126,6 +2126,15 @@ retry:
 		struct hstate *h = folio_hstate(folio);
 		bool adjust_surplus = false;
 
+		/*
+		 * remove_hugetlb_folio()/update_and_free_hugetlb_folio() bail
+		 * for gigantic hstates without runtime support, so dissolving one
+		 * here would leave it on the free list and, on vmemmap restore
+		 * failure, the add_hugetlb_folio() rollback corrupts that list.
+		 */
+		if (hstate_is_gigantic_no_runtime(h))
+			goto out;
+
 		if (!available_huge_pages(h))
 			goto out;
 
author maintainer
#366 lore raw

PATCH 366/395 super: make iterate_supers_type() deletion-safe

In reply to Greg Kroah-Hartman

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

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

From: Christian Brauner <[email protected]>

[ Upstream commit 2d2a2d7aa98741b58f54cacc99b52024e4d865f9 ]

iterate_supers_type() drops sb_lock while invoking the callback and keeps only a passive reference to the current superblock. That reference keeps the object allocated, but does not keep its s_instances node linked.

After the iterator releases s_umount, final teardown can unlink the current s_instances node. The iterator then advances through a reinitialized node. With the current hlist it stops without visiting the remaining superblocks. The unlink moved from generic_shutdown_super() to kill_super_notify(), but the cursor lifetime has been unsafe since the helper was introduced.

The CIFS DFS lookup can consequently miss a matching superblock and return -EINVAL.

Move removal from fs_supers to put_super(), alongside removal from super_blocks, so a passive reference keeps both list nodes linked. Keep the filesystem module reference until then, since unlinking s_instances may touch type->fs_supers.

Make sget_fc() skip SB_DEAD superblocks before invoking test(), and set SB_DEAD under sb_lock to serialize with those callbacks. This allows kernfs to free its private information after kill_anon_super() returns. Keep matching SB_DYING superblocks until SB_DEAD is set so concurrent mounts still wait for teardown before retrying.

Fixes 43e15cdbefea ("new helper: iterate_supers_type()")
[kmehltretter: supplied the commit message]
[ Adapted s_passive reference counting to s_count using the existing __put_super() helper. ]
---
 fs/kernfs/mount.c |    4 ++--
 fs/super.c        |   43 +++++++++++++++++++++++--------------------
 2 files changed, 25 insertions(+), 22 deletions(-)
fs/kernfs/mount.c +2 -2
@@ -433,8 +433,8 @@ void kernfs_kill_sb(struct super_block *
 	up_write(&root->kernfs_supers_rwsem);
 
 	/*
-	 * Remove the superblock from fs_supers/s_instances
-	 * so we can't find it, before freeing kernfs_super_info.
+	 * Mark the superblock dead so sget_fc() can't find it,
+	 * before freeing kernfs_super_info.
 	 */
 	kill_anon_super(sb);
 	kfree(info);
fs/super.c +23 -20

88 lines not on this page · raw message

author maintainer
#367 lore raw

PATCH 367/395 PCI: Fix BAR resize for devices on a root bus

In reply to Greg Kroah-Hartman

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

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

From: Liz Fong-Jones <[email protected]>

[ Upstream commit d58384c22739848efe14b34e9586e4f1242f33c0 ]

pci_do_resource_release_and_resize() releases device BARs that share a bridge window with the BAR being resized, but when the device sits directly on a root bus (pdev->bus->self == NULL) it then skips resource assignment entirely and returns success, leaving the BARs it just released unassigned (IORESOURCE_UNSET).

Skipping pbus_reassign_bridge_resources() is correct in that case -- there is no bridge window to adjust -- but the device BARs still have to be reassigned. Before the BAR release was consolidated into the PCI core, this case worked for amdgpu because the driver released the BARs itself and then called pci_assign_unassigned_bus_resources() unconditionally after the resize, which assigns unassigned device BARs also on a root bus. Commit db92e3fef53e ("drm/amdgpu: Remove driver side BAR release before resize") removed that call, so nothing assigns the released BARs anymore.

This breaks amdgpu completely on the SolidRun HoneyComb LX2K (NXP LX2160A, arm64, ACPI), where ACPI doesn't expose the Root Port so the GPU endpoint appears directly on a "root bus" of its segment:

amdgpu 0004:01:00.0: BAR 0 [mem 0xa400000000-0xa40fffffff 64bit pref]: releasing amdgpu 0004:01:00.0: BAR 2 [mem 0xa410000000-0xa4101fffff 64bit pref]: releasing amdgpu 0004:01:00.0: sw_init of IP block <gmc_v8_0> failed -19 amdgpu 0004:01:00.0: amdgpu_device_ip_init failed
amdgpu 0004:01:00.0: Fatal error during GPU init

No error is logged because the resize path reports success; amdgpu then finds BAR 0 IORESOURCE_UNSET and bails out with -ENODEV.

When there is no upstream bridge, call pci_bus_assign_resources() on the root bus to place the BARs released above, using the same alignment-sorted algorithm as normal enumeration instead of a manual per-BAR loop. This also walks the rest of the hierarchy under the root bus, as pci_assign_unassigned_bus_resources() used to for amdgpu before commit db92e3fef53e ("drm/amdgpu: Remove driver side BAR release before resize") removed that call -- the core-side fix that commit asked for ("such a problem should be fixed inside pci_resize_resource() instead").

pci_bus_assign_resources() returns void, so failure is detected by checking whether the released BARs are still assigned afterward; if not, roll back as in the bridged case. This is stricter than the bridged path -- it fails on any unplaced resource, not just required ones -- since a root bus typically has one shared window, and failing loudly seemed better than leaving something silently unassigned.

The root bus path also had a locking bug that any fix here necessarily
touches: the old "goto out" jumped to up_read(&pci_bus_sem) without a matching down_read() (as does the "goto restore" taken when pci_dev_res_add_to_list() fails in the release loop). Take pci_bus_sem before the BAR release loop so every path through the function holds it exactly once.

Fixes 337b1b566db0 ("PCI: Fix restoring BARs on BAR resize rollback path")
Assisted-by Claude:claude-fable-5 checkpatch
Assisted-by Claude:claude-sonnet-5
[bhelgaas: commit log]
[ Replaced unavailable resource_assigned(dev_res->res) with the equivalent dev_res->res->parent check. ]
---
 drivers/pci/setup-bus.c |   25 ++++++++++++++++++-------
 1 file changed, 18 insertions(+), 7 deletions(-)
drivers/pci/setup-bus.c +18 -7
@@ -2449,6 +2449,7 @@ int pci_do_resource_release_and_resize(s
 	struct resource *res = pci_resource_n(pdev, resno);
 	struct pci_dev_resource *dev_res;
 	struct pci_bus *bus = pdev->bus;
+	struct pci_dev *bridge = pci_upstream_bridge(pdev);
 	struct resource *b_win, *r;
 	LIST_HEAD(saved);
 	unsigned int i;
@@ -2466,6 +2467,8 @@ int pci_do_resource_release_and_resize(s
 	if (ret)
 		return ret;
 
+	down_read(&pci_bus_sem);
+
 	pci_dev_for_each_resource(pdev, r, i) {
 		if (i >= PCI_BRIDGE_RESOURCES)
 			break;
@@ -2484,13 +2487,21 @@ int pci_do_resource_release_and_resize(s
 
 	pci_resize_resource_set_size(pdev, resno, size);
 
-	if (!bus->self)
-		goto out;
-
-	down_read(&pci_bus_sem);
-	ret = pbus_reassign_bridge_resources(bus, res, &saved);
-	if (ret)
-		goto restore;
+	if (bridge) {
+		ret = pbus_reassign_bridge_resources(bus, res, &saved);
+		if (ret)
+			goto restore;
+	} else {
+		/* No bridge window to adjust; let the core reassign the bus. */
+		pci_bus_assign_resources(bus);
+
+		list_for_each_entry(dev_res, &saved, list) {
+			if (!dev_res->res->parent) {
+				ret = -ENOSPC;
+				goto restore;
+			}
+		}
+	}
 
 out:
 	up_read(&pci_bus_sem);
author maintainer
#368 lore raw

PATCH 368/395 HID: hid-alps: Use pm_ptr instead of #ifdef CONFIG_PM

In reply to Greg Kroah-Hartman

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

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

From: Bastien Nocera <[email protected]>

[ Upstream commit 5e130f58629a2819c9d053507ad368160d25eb65 ]

This increases build coverage and allows to drop an #ifdef.

Stable-dep-of: aa9dde93e05a ("HID: alps: unregister DualPoint Stick input device on remove")

---
 drivers/hid/hid-alps.c |    6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)
drivers/hid/hid-alps.c +2 -4
@@ -845,10 +845,8 @@ static struct hid_driver alps_driver = {
 	.raw_event		= alps_raw_event,
 	.input_mapping		= alps_input_mapping,
 	.input_configured	= alps_input_configured,
-#ifdef CONFIG_PM
-	.resume			= alps_post_resume,
-	.reset_resume		= alps_post_reset,
-#endif
+	.resume			= pm_ptr(alps_post_resume),
+	.reset_resume		= pm_ptr(alps_post_reset),
 };
 
 module_hid_driver(alps_driver);
author maintainer
#369 lore raw

PATCH 369/395 HID: alps: unregister DualPoint Stick input device on remove

In reply to Greg Kroah-Hartman

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

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

From: Chen Changcheng <[email protected]>

[ Upstream commit aa9dde93e05a837645fdfd577eea71f0733f0694 ]

alps_input_configured() allocates a second input device ("DualPoint Stick") with input_allocate_device() and registers it, but the alps_driver struct has no .remove handler and input2 is not tracked in hdev->inputs. The default remove path (hid_hw_stop -> hidinput_disconnect) only iterates hdev->inputs, so input2 is never unregistered and leaks on every device removal.

Add a .remove handler that stops the device first (preventing URB callbacks from touching input2 during teardown) and then unregisters input2.

Fixes 2562756dde55 ("HID: add Alps I2C HID Touchpad-Stick support")
---
 drivers/hid/hid-alps.c |   19 +++++++++++++++++++
 1 file changed, 19 insertions(+)
drivers/hid/hid-alps.c +19 -0
@@ -825,6 +825,24 @@ static int alps_probe(struct hid_device
 	return 0;
 }
 
+static void alps_remove(struct hid_device *hdev)
+{
+	struct alps_dev *data = hid_get_drvdata(hdev);
+
+	/*
+	 * input2 ("DualPoint Stick") is allocated separately and is not
+	 * tracked in hdev->inputs, so the default remove path
+	 * (hid_hw_stop -> hidinput_disconnect) does not unregister it.
+	 *
+	 * Stop the device first so that no URB callback can touch input2
+	 * while it is being unregistered, then drop it explicitly.
+	 */
+	hid_hw_stop(hdev);
+
+	if (data->input2)
+		input_unregister_device(data->input2);
+}
+
 static const struct hid_device_id alps_id[] = {
 	{ HID_DEVICE(HID_BUS_ANY, HID_GROUP_ANY,
 		USB_VENDOR_ID_ALPS_JP, HID_DEVICE_ID_ALPS_U1_DUAL) },
@@ -847,6 +865,7 @@ static struct hid_driver alps_driver = {
 	.input_configured	= alps_input_configured,
 	.resume			= pm_ptr(alps_post_resume),
 	.reset_resume		= pm_ptr(alps_post_reset),
+	.remove			= alps_remove,
 };
 
 module_hid_driver(alps_driver);
author maintainer
#370 lore raw

PATCH 370/395 net: macb: fix dma_alloc_coherent() leak on macb_alloc() error paths

In reply to Greg Kroah-Hartman

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

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

From: Théo Lebrun <[email protected]>

[ Upstream commit 23d42b9a3bcd55b17d3b371544fedc708c2397e9 ]

Fix 3 leaks in macb_alloc() error paths:
- Tx buffer allocated but crossing a 4G boundary: Tx leaked.
- Rx buffer allocation fails: Tx leaked.
- Rx buffer allocated but crossing a 4G boundary: Tx & Rx leaked.

This is because our error handling calls macb_free(bp) which in turn frees the buffers stored in bp->queues[0], but nothing has been stored in there. Fix by storing allocated buffers into bp->queues[0] ASAP.

---
 drivers/net/ethernet/cadence/macb_main.c |   14 ++++++++++++--
 1 file changed, 12 insertions(+), 2 deletions(-)
drivers/net/ethernet/cadence/macb_main.c +12 -2
@@ -2663,14 +2663,24 @@ static int macb_alloc_consistent(struct
 
 	size = bp->num_queues * macb_tx_ring_size_per_queue(bp);
 	tx = dma_alloc_coherent(dev, size, &tx_dma, GFP_KERNEL);
-	if (!tx || upper_32_bits(tx_dma) != upper_32_bits(tx_dma + size - 1))
+	if (!tx)
+		goto out_err;
+	/* Record the buffer so that the error path frees it. */
+	bp->queues[0].tx_ring = tx;
+	bp->queues[0].tx_ring_dma = tx_dma;
+	if (upper_32_bits(tx_dma) != upper_32_bits(tx_dma + size - 1))
 		goto out_err;
 	netdev_dbg(bp->dev, "Allocated %zu bytes for %u TX rings at %08lx (mapped %p)\n",
 		   size, bp->num_queues, (unsigned long)tx_dma, tx);
 
 	size = bp->num_queues * macb_rx_ring_size_per_queue(bp);
 	rx = dma_alloc_coherent(dev, size, &rx_dma, GFP_KERNEL);
-	if (!rx || upper_32_bits(rx_dma) != upper_32_bits(rx_dma + size - 1))
+	if (!rx)
+		goto out_err;
+	/* Record the buffer so that the error path frees it. */
+	bp->queues[0].rx_ring = rx;
+	bp->queues[0].rx_ring_dma = rx_dma;
+	if (upper_32_bits(rx_dma) != upper_32_bits(rx_dma + size - 1))
 		goto out_err;
 	netdev_dbg(bp->dev, "Allocated %zu bytes for %u RX rings at %08lx (mapped %p)\n",
 		   size, bp->num_queues, (unsigned long)rx_dma, rx);
author maintainer
#371 lore raw

PATCH 371/395 net: ipconfig: Remove outdated comment and indent code block

In reply to Greg Kroah-Hartman

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

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

From: Thorsten Blum <[email protected]>

[ Upstream commit e405b3c9d4aaa10972525fe2ea5cf94224020561 ]

The comment has been around ever since commit 1da177e4c3f4 ("Linux-2.6.12-rc2") and can be removed. Remove it and indent the code block accordingly.

Stable-dep-of: e47a1958e12a ("net: ipconfig: bound DHCP option construction")

---
 net/ipv4/ipconfig.c |   89 ++++++++++++++++++++++++----------------------------
 1 file changed, 42 insertions(+), 47 deletions(-)
net/ipv4/ipconfig.c +42 -47
@@ -679,8 +679,18 @@ static const u8 ic_bootp_cookie[4] = { 9
 static void __init
 ic_dhcp_init_options(u8 *options, struct ic_device *d)
 {
-	u8 mt = ((ic_servaddr == NONE)
-		 ? DHCPDISCOVER : DHCPREQUEST);
+	static const u8 ic_req_params[] = {
+		1,	/* Subnet mask */
+		3,	/* Default gateway */
+		6,	/* DNS server */
+		12,	/* Host name */
+		15,	/* Domain name */
+		17,	/* Boot path */
+		26,	/* MTU */
+		40,	/* NIS domain name */
+		42,	/* NTP servers */
+	};
+	u8 mt = (ic_servaddr == NONE) ? DHCPDISCOVER : DHCPREQUEST;
 	u8 *e = options;
 	int len;
 
@@ -705,51 +715,36 @@ ic_dhcp_init_options(u8 *options, struct
 		e += 4;
 	}
 
-	/* always? */
-	{
-		static const u8 ic_req_params[] = {
-			1,	/* Subnet mask */
-			3,	/* Default gateway */
-			6,	/* DNS server */
-			12,	/* Host name */
-			15,	/* Domain name */
-			17,	/* Boot path */
-			26,	/* MTU */
-			40,	/* NIS domain name */
-			42,	/* NTP servers */
-		};
-
-		*e++ = 55;	/* Parameter request list */
-		*e++ = sizeof(ic_req_params);
-		memcpy(e, ic_req_params, sizeof(ic_req_params));
-		e += sizeof(ic_req_params);
-
-		if (ic_host_name_set) {
-			*e++ = 12;	/* host-name */
-			len = strlen(utsname()->nodename);
-			*e++ = len;
-			memcpy(e, utsname()->nodename, len);
-			e += len;
-		}
-		if (*vendor_class_identifier) {
-			pr_info("DHCP: sending class identifier \"%s\"\n",
-				vendor_class_identifier);
-			*e++ = 60;	/* Class-identifier */
-			len = strlen(vendor_class_identifier);
-			*e++ = len;
-			memcpy(e, vendor_class_identifier, len);
-			e += len;
-		}
-		len = strlen(dhcp_client_identifier + 1);
-		/* the minimum length of identifier is 2, include 1 byte type,
-		 * and can not be larger than the length of options
-		 */
-		if (len >= 1 && len < 312 - (e - options) - 1) {
-			*e++ = 61;
-			*e++ = len + 1;
-			memcpy(e, dhcp_client_identifier, len + 1);
-			e += len + 1;
-		}
+	*e++ = 55;	/* Parameter request list */
+	*e++ = sizeof(ic_req_params);
+	memcpy(e, ic_req_params, sizeof(ic_req_params));
+	e += sizeof(ic_req_params);
+
+	if (ic_host_name_set) {
+		*e++ = 12;	/* host-name */
+		len = strlen(utsname()->nodename);
+		*e++ = len;
+		memcpy(e, utsname()->nodename, len);
+		e += len;
+	}
+	if (*vendor_class_identifier) {
+		pr_info("DHCP: sending class identifier \"%s\"\n",
+			vendor_class_identifier);
+		*e++ = 60;	/* Class-identifier */
+		len = strlen(vendor_class_identifier);
+		*e++ = len;
+		memcpy(e, vendor_class_identifier, len);
+		e += len;
+	}
+	len = strlen(dhcp_client_identifier + 1);
+	/* the minimum length of identifier is 2, include 1 byte type,
+	 * and can not be larger than the length of options
+	 */
+	if (len >= 1 && len < 312 - (e - options) - 1) {
+		*e++ = 61;
+		*e++ = len + 1;
+		memcpy(e, dhcp_client_identifier, len + 1);
+		e += len + 1;
 	}
 
 	*e++ = 255;	/* End of the list */
author maintainer
#372 lore raw

PATCH 372/395 net: ipconfig: bound DHCP option construction

In reply to Greg Kroah-Hartman

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

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

From: Yuqi Xu <[email protected]>

[ Upstream commit e47a1958e12abc3a17b5231a4f21c8f1bf662e08 ]

ic_dhcp_init_options() appends the hostname (option 12), vendor-class (option 60) and client-ID (option 61) options into the fixed 312-byte bootp_pkt.exten[] buffer. Only the client-ID branch checked the remaining space; the hostname and vendor-class writes were unbounded.

A 64-byte hostname together with the maximum 252-byte dhcpclass= identifier needs 18 + (2 + 64) + (2 + 252) = 338 of the 312 available bytes even before the terminating END marker, so the vendor-class memcpy runs past the end of exten[]. With CONFIG_FORTIFY_SOURCE this is reported as a field-spanning write and, when the kernel is booted with panic_on_warn=1, aborts boot with a panic.

Route the optional options through a common helper that makes sure the option, its 2-byte header and the END marker all fit and drops an option that would not. Configurations with short options keep sending exactly the same bytes as before.

---
 net/ipv4/ipconfig.c |   45 ++++++++++++++++++++++++++-------------------
 1 file changed, 26 insertions(+), 19 deletions(-)
net/ipv4/ipconfig.c +26 -19
@@ -676,6 +676,24 @@ static const u8 ic_bootp_cookie[4] = { 9
 
 #ifdef IPCONFIG_DHCP
 
+static bool __init
+ic_dhcp_add_option(u8 **options, const u8 *end, u8 type, const void *value,
+		   int len)
+{
+	u8 *e = *options;
+
+	/* leave room for the option header and the END marker */
+	if (len > U8_MAX || end - e < len + 3)
+		return false;
+
+	*e++ = type;
+	*e++ = len;
+	memcpy(e, value, len);
+	*options = e + len;
+
+	return true;
+}
+
 static void __init
 ic_dhcp_init_options(u8 *options, struct ic_device *d)
 {
@@ -691,6 +709,7 @@ ic_dhcp_init_options(u8 *options, struct
 		42,	/* NTP servers */
 	};
 	u8 mt = (ic_servaddr == NONE) ? DHCPDISCOVER : DHCPREQUEST;
+	u8 *end = options + sizeof(((struct bootp_pkt *)0)->exten);
 	u8 *e = options;
 	int len;
 
@@ -721,31 +740,19 @@ ic_dhcp_init_options(u8 *options, struct
 	e += sizeof(ic_req_params);
 
 	if (ic_host_name_set) {
-		*e++ = 12;	/* host-name */
 		len = strlen(utsname()->nodename);
-		*e++ = len;
-		memcpy(e, utsname()->nodename, len);
-		e += len;
+		ic_dhcp_add_option(&e, end, 12, utsname()->nodename, len);
 	}
 	if (*vendor_class_identifier) {
-		pr_info("DHCP: sending class identifier \"%s\"\n",
-			vendor_class_identifier);
-		*e++ = 60;	/* Class-identifier */
 		len = strlen(vendor_class_identifier);
-		*e++ = len;
-		memcpy(e, vendor_class_identifier, len);
-		e += len;
+		if (ic_dhcp_add_option(&e, end, 60, vendor_class_identifier, len))
+			pr_info("DHCP: sending class identifier \"%s\"\n",
+				vendor_class_identifier);
 	}
 	len = strlen(dhcp_client_identifier + 1);
-	/* the minimum length of identifier is 2, include 1 byte type,
-	 * and can not be larger than the length of options
-	 */
-	if (len >= 1 && len < 312 - (e - options) - 1) {
-		*e++ = 61;
-		*e++ = len + 1;
-		memcpy(e, dhcp_client_identifier, len + 1);
-		e += len + 1;
-	}
+	/* the minimum length of identifier is 2, include 1 byte type */
+	if (len >= 1)
+		ic_dhcp_add_option(&e, end, 61, dhcp_client_identifier, len + 1);
 
 	*e++ = 255;	/* End of the list */
 }
author maintainer
#373 lore raw

PATCH 373/395 perf/x86/intel: Update event constraints and cache_extra_regsfor ADL

In reply to Greg Kroah-Hartman

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

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

From: Dapeng Mi <[email protected]>

[ Upstream commit 4ef863352bcde482d65722ed721c3fd3967a67d6 ]

Update perf hard-coded event constraints and cache_extra_regs[] for Alderlake according to the latest ADL perfmon events (V1.39).

One important note is that ADL has differences on the L3/node related OCR events although it shares same uarch with SPR server, e.g., ADL has different extra MSR values and no node events. So some variants of structures and functions are introduced to reflect these differences, like adl_glc_hw_cache_event_ids[], adl_glc_hw_cache_extra_regs[] and intel_pmu_init_glc_hybrid(), etc.

Please note these changes would temporarily impact other platforms like MTL/ARL-U which shares hard-coded event structures, but it would be fixed soon in subsequent patches.

ADL perfmon events:
https://github.com/intel/perfmon/blob/main/ADL/events/alderlake_goldencove_core.json
https://github.com/intel/perfmon/blob/main/ADL/events/alderlake_gracemont_core.json

[Stable dependency adaptation]
Retain only the Gracemont STORE_LATENCY (event 0x6d0) PEBS counter-mask update from 0xf to 0x3f in intel_grt_pebs_event_constraints[]. This is the preimage required by target 89dc568e8c0be60e05e5fcd0b528c79077d7b84b. The existing latency decoder and LOAD/STORE constraint macros are already available in this stable tree, so no new functions are needed.

Drop all core.c changes: the cache-event tables, offcore MSR masks, fixed-event aliases and hybrid initialization changes are unrelated to the target. Their upstream context includes cache tables absent from 6.18, and retaining them would bring in an unnecessary helper function and broader platform changes.

[ sashal: Reduced backport -- upstream 4ef863352bcde touches 2 file(s), this
backport carries 1. Not backported here:
arch/x86/events/intel/core.c
This note is generated from the file lists only; see the resolution record for the reasoning. ]

Stable-dep-of: 89dc568e8c0b ("perf/x86/intel: Fix GRT PEBS load/store direction for latency events, to fix sample classification")

---
 arch/x86/events/intel/ds.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
arch/x86/events/intel/ds.c +1 -1
@@ -1011,7 +1011,7 @@ struct event_constraint intel_glm_pebs_e
 struct event_constraint intel_grt_pebs_event_constraints[] = {
 	/* Allow all events as PEBS with no flags */
 	INTEL_HYBRID_LAT_CONSTRAINT(0x5d0, 0x3),
-	INTEL_HYBRID_LAT_CONSTRAINT(0x6d0, 0xf),
+	INTEL_HYBRID_LAT_CONSTRAINT(0x6d0, 0x3f),
 	EVENT_CONSTRAINT_END
 };
 
author maintainer
#374 lore raw

PATCH 374/395 perf/x86/intel: Fix GRT PEBS load/store direction for latency events, to fix sample classification

In reply to Greg Kroah-Hartman

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

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

From: Dapeng Mi <[email protected]>

[ Upstream commit 89dc568e8c0be60e05e5fcd0b528c79077d7b84b ]

On Gracemont, intel_grt_pebs_event_constraints[] applies LAT_CONSTRAINT constraints to MEM_UOPS_RETIRED.{LOAD,STORE}_LATENCY, but does not set explicit LOAD/STORE flags for those events.

The PEBS latency path (pebs_latency_data(), via __grt_latency_data()) uses the event flags to determine memory operation direction. Without an explicit STORE flag, samples from MEM_UOPS_RETIRED.STORE_LATENCY can be misclassified as LOADs.

Set explicit LOAD/STORE flags in intel_grt_pebs_event_constraints[] for:

- MEM_UOPS_RETIRED.LOAD_LATENCY
- MEM_UOPS_RETIRED.STORE_LATENCY

Also update __grt_latency_data() to explicitly interpret these flags when assigning the sampled memory operation direction.

This fixes incorrect STORE sample classification.

Fixes 39a41278f041 ("perf/x86/intel: Fix PEBS memory access info encoding for ADL")
Cc <[email protected]> # v7.2+
---
 arch/x86/events/intel/ds.c |   16 +++++++++++++---
 1 file changed, 13 insertions(+), 3 deletions(-)
arch/x86/events/intel/ds.c +13 -3
@@ -315,6 +315,7 @@ static inline void pebs_set_tlb_lock(u64
 static u64 __grt_latency_data(struct perf_event *event, u64 status,
 			       u8 dse, bool tlb, bool lock, bool blk)
 {
+	union perf_mem_data_src src;
 	u64 val;
 
 	WARN_ON_ONCE(is_hybrid() &&
@@ -330,7 +331,16 @@ static u64 __grt_latency_data(struct per
 	else
 		val |= P(BLK, NA);
 
-	return val;
+	src.val = val;
+
+	if (event->hw.flags &
+	    (PERF_X86_EVENT_PEBS_LDLAT | PERF_X86_EVENT_PEBS_LD_HSW))
+		src.mem_op = P(OP, LOAD);
+	if (event->hw.flags &
+	    (PERF_X86_EVENT_PEBS_STLAT | PERF_X86_EVENT_PEBS_ST_HSW))
+		src.mem_op = P(OP, STORE);
+
+	return src.val;
 }
 
 u64 grt_latency_data(struct perf_event *event, u64 status)
@@ -1010,8 +1020,8 @@ struct event_constraint intel_glm_pebs_e
 
 struct event_constraint intel_grt_pebs_event_constraints[] = {
 	/* Allow all events as PEBS with no flags */
-	INTEL_HYBRID_LAT_CONSTRAINT(0x5d0, 0x3),
-	INTEL_HYBRID_LAT_CONSTRAINT(0x6d0, 0x3f),
+	INTEL_HYBRID_LDLAT_CONSTRAINT(0x5d0, 0x3),
+	INTEL_HYBRID_STLAT_CONSTRAINT(0x6d0, 0x3f),
 	EVENT_CONSTRAINT_END
 };
 
author maintainer
#375 lore raw

PATCH 375/395 perf/x86/intel: Update event constraints and cache_extra_regsfor LNL

In reply to Greg Kroah-Hartman

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

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

From: Dapeng Mi <[email protected]>

[ Upstream commit 331c3e4fa39a87560c09bdd878652090ae040b69 ]

Update perf hard-coded event constraints and cache_extra_regs[] for Lunarlake according to the latest LNL perfmon events (V1.22).

LNL introduces new extra register values for the OCR L3 cache events, so introduce lnc_hw_cache_extra_regs[] and skt_hw_cache_extra_regs[] to reflect the changes.

LNL perfmon events:
https://github.com/intel/perfmon/blob/main/LNL/events/lunarlake_lioncove_core.json
https://github.com/intel/perfmon/blob/main/LNL/events/lunarlake_skymont_core.json

[Backport to 6.18: retain the Lion Cove PEBS constraint additions needed by 7c944595cc43 ("perf/x86/intel: Remove incorrect LionCove PEBS data-source constraints"). Keep the matching regular counter masks for TOPDOWN.MEMORY_BOUND_SLOTS (0x10a4, 0x8) and MEM_INST_RETIRED.ANY (0x87d0, 0x3ff), since that fix removes their PEBS-specific entries and falls back to the regular constraint table.

Omit the unrelated cache extra-register tables and PMU initialization changes, which depend on cache-event refactoring and intel_pmu_init_cmt() absent from 6.18. Also omit the unrelated fixed-counter, non-PEBS OCR, 0x00e0, and Skymont constraint updates. No functions are added.]

Stable-dep-of: 7c944595cc43 ("perf/x86/intel: Remove incorrect LionCove PEBS data-source constraints")

---
 arch/x86/events/intel/core.c |    3 ++-
 arch/x86/events/intel/ds.c   |    8 ++++++++
 2 files changed, 10 insertions(+), 1 deletion(-)
arch/x86/events/intel/core.c +2 -1
@@ -412,11 +412,12 @@ static struct event_constraint intel_lnc
 	INTEL_UEVENT_CONSTRAINT(0x0ca3, 0x4),
 	INTEL_UEVENT_CONSTRAINT(0x04a4, 0x1),
 	INTEL_UEVENT_CONSTRAINT(0x08a4, 0x1),
-	INTEL_UEVENT_CONSTRAINT(0x10a4, 0x1),
+	INTEL_UEVENT_CONSTRAINT(0x10a4, 0x8),
 	INTEL_UEVENT_CONSTRAINT(0x01b1, 0x8),
 	INTEL_UEVENT_CONSTRAINT(0x01cd, 0x3fc),
 	INTEL_UEVENT_CONSTRAINT(0x02cd, 0x3),
 
+	INTEL_UEVENT_CONSTRAINT(0x87d0, 0x3ff),
 	INTEL_EVENT_CONSTRAINT_RANGE(0xd0, 0xdf, 0xf),
 
 	INTEL_UEVENT_CONSTRAINT(0x00e0, 0xf),
arch/x86/events/intel/ds.c +8 -0
@@ -1218,6 +1218,13 @@ struct event_constraint intel_lnc_pebs_e
 	INTEL_FLAGS_UEVENT_CONSTRAINT(0x100, 0x100000000ULL),	/* INST_RETIRED.PREC_DIST */
 	INTEL_FLAGS_UEVENT_CONSTRAINT(0x0400, 0x800000000ULL),
 
+	INTEL_FLAGS_UEVENT_CONSTRAINT(0x012a, 0x1),		/* OCR.* events */
+	INTEL_FLAGS_UEVENT_CONSTRAINT(0x012b, 0x1),		/* OCR.* events */
+
+	INTEL_FLAGS_UEVENT_CONSTRAINT(0x04a4, 0x1),		/* TOPDOWN.BAD_SPEC_SLOTS */
+	INTEL_FLAGS_UEVENT_CONSTRAINT(0x08a4, 0x1),		/* TOPDOWN.BR_MISPREDICT_SLOTS */
+	INTEL_FLAGS_UEVENT_CONSTRAINT(0x10a4, 0x8),		/* TOPDOWN.MEMORY_BOUND_SLOTS */
+
 	INTEL_HYBRID_LDLAT_CONSTRAINT(0x1cd, 0x3fc),
 	INTEL_HYBRID_STLAT_CONSTRAINT(0x2cd, 0x3),
 	INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_LD(0x11d0, 0xf),	/* MEM_INST_RETIRED.STLB_MISS_LOADS */
@@ -1227,6 +1234,7 @@ struct event_constraint intel_lnc_pebs_e
 	INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_ST(0x42d0, 0xf),	/* MEM_INST_RETIRED.SPLIT_STORES */
 	INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_LD(0x81d0, 0xf),	/* MEM_INST_RETIRED.ALL_LOADS */
 	INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_ST(0x82d0, 0xf),	/* MEM_INST_RETIRED.ALL_STORES */
+	INTEL_FLAGS_UEVENT_CONSTRAINT(0x87d0, 0x3ff),		/* MEM_INST_RETIRED.ANY */
 
 	INTEL_FLAGS_EVENT_CONSTRAINT_DATALA_LD_RANGE(0xd1, 0xd4, 0xf),
 
author maintainer
#376 lore raw

PATCH 376/395 perf/x86/intel: Remove incorrect LionCove PEBS data-source constraints

In reply to Greg Kroah-Hartman

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

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

From: Dapeng Mi <[email protected]>

[ Upstream commit 7c944595cc43a664019edc22505b6d6f6039be5e ]

On Lion Cove, PEBS data source is valid only for these events:

- MEM_TRANS_RETIRED.LOAD_LATENCY (0x1cd)
- MEM_TRANS_RETIRED.STORE_SAMPLE (0x2cd)

The perfmon database (https://github.com/intel/perfmon) previously tagged additional memory events such as MEM_INST_RETIRED.STLB_MISS_LOADS with L1_Hit_Indication, implying PEBS data-source support, which is incorrect. The database has since been fixed, but intel_lnc_pebs_event_constraints[] still follows the old definition and marks those events as data-source capable.

As a result, get_data_src() may decode data-source information for events that do not provide valid PEBS data-source data and mislead users.

Remove those non-data-source memory events from the Lion Cove PEBS constraint table so matching falls back to the regular non-PEBS constraints, which already provide the same counter constraints.

Also update lnc_latency_data() to decode LOAD/STORE flags explicitly when setting memory operation direction, for consistency with other *_latency_data() helpers.

---
 arch/x86/events/intel/ds.c |   22 +++++-----------------
 1 file changed, 5 insertions(+), 17 deletions(-)
arch/x86/events/intel/ds.c +5 -17
@@ -395,7 +395,11 @@ static u64 lnc_latency_data(struct perf_
 		val |= P(BLK, NA);
 
 	src.val = val;
-	if (event->hw.flags & PERF_X86_EVENT_PEBS_ST_HSW)
+	if (event->hw.flags &
+	    (PERF_X86_EVENT_PEBS_LDLAT | PERF_X86_EVENT_PEBS_LD_HSW))
+		src.mem_op = P(OP, LOAD);
+	if (event->hw.flags &
+	    (PERF_X86_EVENT_PEBS_STLAT | PERF_X86_EVENT_PEBS_ST_HSW))
 		src.mem_op = P(OP, STORE);
 
 	return src.val;
@@ -1221,24 +1225,8 @@ struct event_constraint intel_lnc_pebs_e
 	INTEL_FLAGS_UEVENT_CONSTRAINT(0x012a, 0x1),		/* OCR.* events */
 	INTEL_FLAGS_UEVENT_CONSTRAINT(0x012b, 0x1),		/* OCR.* events */
 
-	INTEL_FLAGS_UEVENT_CONSTRAINT(0x04a4, 0x1),		/* TOPDOWN.BAD_SPEC_SLOTS */
-	INTEL_FLAGS_UEVENT_CONSTRAINT(0x08a4, 0x1),		/* TOPDOWN.BR_MISPREDICT_SLOTS */
-	INTEL_FLAGS_UEVENT_CONSTRAINT(0x10a4, 0x8),		/* TOPDOWN.MEMORY_BOUND_SLOTS */
-
 	INTEL_HYBRID_LDLAT_CONSTRAINT(0x1cd, 0x3fc),
 	INTEL_HYBRID_STLAT_CONSTRAINT(0x2cd, 0x3),
-	INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_LD(0x11d0, 0xf),	/* MEM_INST_RETIRED.STLB_MISS_LOADS */
-	INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_ST(0x12d0, 0xf),	/* MEM_INST_RETIRED.STLB_MISS_STORES */
-	INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_LD(0x21d0, 0xf),	/* MEM_INST_RETIRED.LOCK_LOADS */
-	INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_LD(0x41d0, 0xf),	/* MEM_INST_RETIRED.SPLIT_LOADS */
-	INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_ST(0x42d0, 0xf),	/* MEM_INST_RETIRED.SPLIT_STORES */
-	INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_LD(0x81d0, 0xf),	/* MEM_INST_RETIRED.ALL_LOADS */
-	INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_ST(0x82d0, 0xf),	/* MEM_INST_RETIRED.ALL_STORES */
-	INTEL_FLAGS_UEVENT_CONSTRAINT(0x87d0, 0x3ff),		/* MEM_INST_RETIRED.ANY */
-
-	INTEL_FLAGS_EVENT_CONSTRAINT_DATALA_LD_RANGE(0xd1, 0xd4, 0xf),
-
-	INTEL_FLAGS_EVENT_CONSTRAINT(0xd0, 0xf),
 
 	/*
 	 * Everything else is handled by PMU_FL_PEBS_ALL, because we
author maintainer
#377 lore raw

PATCH 377/395 x86/sev: Move the internal header

In reply to Greg Kroah-Hartman

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

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

From: "Borislav Petkov (AMD)" <[email protected]>

[ Upstream commit c1e8980fabf5d0106992a430284fac28bba053a6 ]

Move the internal header out of the usual include/asm/ include path because having an "internal" header there doesn't really make it internal - quite the opposite - that's the normal arch include path.

So move where it belongs and make it really internal.

No functional changes.

Stable-dep-of: 6c43c72748ff ("x86/sev: Make vTPM SVSM calls preemption-safe")

---
 arch/x86/boot/startup/sev-startup.c                          | 3 ++-
 arch/x86/coco/sev/core.c                                     | 3 ++-
 arch/x86/{include/asm/sev-internal.h => coco/sev/internal.h} | 0
 arch/x86/boot/startup/sev-startup.c |    3 -
 arch/x86/coco/sev/core.c            |    3 -
 arch/x86/coco/sev/internal.h        |   87 ++++++++++++++++++++++++++++++++++++
 arch/x86/coco/sev/noinstr.c         |    3 -
 arch/x86/coco/sev/vc-handle.c       |    3 -
 arch/x86/include/asm/sev-internal.h |   87 ------------------------------------
 6 files changed, 95 insertions(+), 91 deletions(-)
 rename arch/x86/{include/asm/sev-internal.h => coco/sev/internal.h} (100%)
arch/x86/boot/startup/sev-startup.c +2 -1
@@ -27,7 +27,6 @@
 #include <asm/cpu_entry_area.h>
 #include <asm/stacktrace.h>
 #include <asm/sev.h>
-#include <asm/sev-internal.h>
 #include <asm/insn-eval.h>
 #include <asm/fpu/xcr.h>
 #include <asm/processor.h>
@@ -41,6 +40,8 @@
 #include <asm/cpuid/api.h>
 #include <asm/cmdline.h>
 
+#include "../../coco/sev/internal.h"
+
 /* Include code shared with pre-decompression boot stage */
 #include "sev-shared.c"
 
arch/x86/coco/sev/core.c +2 -1
@@ -31,7 +31,6 @@
 #include <asm/cpu_entry_area.h>
 #include <asm/stacktrace.h>
 #include <asm/sev.h>
-#include <asm/sev-internal.h>
 #include <asm/insn-eval.h>
 #include <asm/fpu/xcr.h>
 #include <asm/processor.h>
@@ -46,6 +45,8 @@
 #include <asm/cmdline.h>
 #include <asm/msr.h>
 
+#include "internal.h"
+
 /* Bitmap of SEV features supported by the hypervisor */
 u64 sev_hv_features __ro_after_init;
 SYM_PIC_ALIAS(sev_hv_features);
arch/x86/coco/sev/internal.h new file +87 -0

87 lines not on this page · raw message

arch/x86/coco/sev/noinstr.c +2 -1
@@ -16,7 +16,8 @@
 #include <asm/msr.h>
 #include <asm/ptrace.h>
 #include <asm/sev.h>
-#include <asm/sev-internal.h>
+
+#include "internal.h"
 
 static __always_inline bool on_vc_stack(struct pt_regs *regs)
 {
arch/x86/coco/sev/vc-handle.c +2 -1
@@ -23,7 +23,6 @@
 #include <asm/init.h>
 #include <asm/stacktrace.h>
 #include <asm/sev.h>
-#include <asm/sev-internal.h>
 #include <asm/insn-eval.h>
 #include <asm/fpu/xcr.h>
 #include <asm/processor.h>
@@ -35,6 +34,8 @@
 #include <asm/apic.h>
 #include <asm/cpuid/api.h>
 
+#include "internal.h"
+
 static enum es_result vc_slow_virt_to_phys(struct ghcb *ghcb, struct es_em_ctxt *ctxt,
 					   unsigned long vaddr, phys_addr_t *paddr)
 {
arch/x86/include/asm/sev-internal.h deleted +0 -87

87 lines not on this page · raw message

author maintainer
#378 lore raw

PATCH 378/395 x86/sev: Add internal header guards

In reply to Greg Kroah-Hartman

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

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

From: "Borislav Petkov (AMD)" <[email protected]>

[ Upstream commit f01c6489ad6ceb8d06eae0c5123fc6cf39276ff1 ]

All headers need guards ifdeffery.

Stable-dep-of: 6c43c72748ff ("x86/sev: Make vTPM SVSM calls preemption-safe")

---
 arch/x86/coco/sev/internal.h |    3 +++
 1 file changed, 3 insertions(+)
arch/x86/coco/sev/internal.h +3 -0
@@ -1,4 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __X86_COCO_SEV_INTERNAL_H__
+#define __X86_COCO_SEV_INTERNAL_H__
 
 #define DR7_RESET_VALUE        0x400
 
@@ -85,3 +87,4 @@ enum es_result sev_es_ghcb_handle_msr(st
 u64 get_hv_features(void);
 
 const struct snp_cpuid_table *snp_cpuid_get_table(void);
+#endif /* __X86_COCO_SEV_INTERNAL_H__ */
author maintainer
#379 lore raw

PATCH 379/395 x86/sev: Carve out the SVSM code into a separate compilation unit

In reply to Greg Kroah-Hartman

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

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

From: "Borislav Petkov (AMD)" <[email protected]>

[ Upstream commit e21279b73ef6c7d27237912c914f2db0c5a74786 ]

Move the SVSM-related machinery into a separate compilation unit in order to keep sev/core.c slim and "on-topic".

No functional changes.

Stable-dep-of: 6c43c72748ff ("x86/sev: Make vTPM SVSM calls preemption-safe")

---
 arch/x86/coco/sev/Makefile   |    2 
 arch/x86/coco/sev/core.c     |  377 -------------------------------------------
 arch/x86/coco/sev/internal.h |   29 +++
 arch/x86/coco/sev/svsm.c     |  362 +++++++++++++++++++++++++++++++++++++++++
 4 files changed, 392 insertions(+), 378 deletions(-)
 create mode 100644 arch/x86/coco/sev/svsm.c
arch/x86/coco/sev/Makefile +1 -1
@@ -1,6 +1,6 @@
 # SPDX-License-Identifier: GPL-2.0
 
-obj-y += core.o noinstr.o vc-handle.o
+obj-y += core.o noinstr.o vc-handle.o svsm.o
 
 # Clang 14 and older may fail to respect __no_sanitize_undefined when inlining
 UBSAN_SANITIZE_noinstr.o	:= n
arch/x86/coco/sev/core.c +0 -377

407 lines not on this page · raw message

arch/x86/coco/sev/internal.h +29 -0
@@ -66,6 +66,9 @@ extern u64 boot_svsm_caa_pa;
 
 enum es_result verify_exception_info(struct ghcb *ghcb, struct es_em_ctxt *ctxt);
 void vc_forward_exception(struct es_em_ctxt *ctxt);
+void svsm_pval_pages(struct snp_psc_desc *desc);
+int svsm_perform_call_protocol(struct svsm_call *call);
+bool snp_svsm_vtpm_probe(void);
 
 static inline u64 sev_es_rd_ghcb_msr(void)
 {
@@ -87,4 +90,30 @@ enum es_result sev_es_ghcb_handle_msr(st
 u64 get_hv_features(void);
 
 const struct snp_cpuid_table *snp_cpuid_get_table(void);
+
+static inline struct svsm_ca *svsm_get_caa(void)
+{
+	if (sev_cfg.use_cas)
+		return this_cpu_read(svsm_caa);
+	else
+		return rip_rel_ptr(&boot_svsm_ca_page);
+}
+
+static inline u64 svsm_get_caa_pa(void)
+{
+	if (sev_cfg.use_cas)
+		return this_cpu_read(svsm_caa_pa);
+	else
+		return boot_svsm_caa_pa;
+}
+
+static inline void __pval_terminate(u64 pfn, bool action, unsigned int page_size,
+				    int ret, u64 svsm_ret)
+{
+	WARN(1, "PVALIDATE failure: pfn: 0x%llx, action: %u, size: %u, ret: %d, svsm_ret: 0x%llx\n",
+	     pfn, action, page_size, ret, svsm_ret);
+
+	sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_PVALIDATE);
+}
+
 #endif /* __X86_COCO_SEV_INTERNAL_H__ */
arch/x86/coco/sev/svsm.c new file +362 -0

362 lines not on this page · raw message

author maintainer
#380 lore raw

PATCH 380/395 x86/sev: Remove redundant ghcbs_initialized checks around __sev_{get,put}_ghcb()

In reply to Greg Kroah-Hartman

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

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

From: Nikunj A Dadhania <[email protected]>

[ Upstream commit 9d8460a1c7a6b0f2dc6302e5d0f31d4e8c2a7913 ]

After

3645eb7e3915 ("x86/fred: Fix early boot failures on SEV-ES/SNP guests"),

__sev_{get,put}_ghcb() handle the early-boot GHCB fallback internally, making the ghcbs_initialized guards in __set_pages_state() and svsm_perform_call_protocol() redundant.

Remove them.

Also initialize state->ghcb to NULL in the early-boot path of __sev_get_ghcb() so that the ghcb_state is well-defined for all callers, even though __sev_put_ghcb() currently returns early before reading it.

No functional change intended.

Stable-dep-of: 6c43c72748ff ("x86/sev: Make vTPM SVSM calls preemption-safe")

---
 arch/x86/coco/sev/core.c    |    8 ++------
 arch/x86/coco/sev/noinstr.c |    4 +++-
 arch/x86/coco/sev/svsm.c    |   10 ++--------
 3 files changed, 7 insertions(+), 15 deletions(-)
arch/x86/coco/sev/core.c +2 -6
@@ -367,17 +367,13 @@ static unsigned long __set_pages_state(s
 
 	local_irq_save(flags);
 
-	if (sev_cfg.ghcbs_initialized)
-		ghcb = __sev_get_ghcb(&state);
-	else
-		ghcb = boot_ghcb;
+	ghcb = __sev_get_ghcb(&state);
 
 	/* Invoke the hypervisor to perform the page state changes */
 	if (!ghcb || vmgexit_psc(ghcb, data))
 		sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_PSC);
 
-	if (sev_cfg.ghcbs_initialized)
-		__sev_put_ghcb(&state);
+	__sev_put_ghcb(&state);
 
 	local_irq_restore(flags);
 
arch/x86/coco/sev/noinstr.c +3 -1
@@ -121,8 +121,10 @@ noinstr struct ghcb *__sev_get_ghcb(stru
 
 	WARN_ON(!irqs_disabled());
 
-	if (!sev_cfg.ghcbs_initialized)
+	if (!sev_cfg.ghcbs_initialized) {
+		state->ghcb = NULL;
 		return boot_ghcb;
+	}
 
 	data = this_cpu_read(runtime_data);
 	ghcb = &data->ghcb_page;
arch/x86/coco/sev/svsm.c +2 -8
@@ -74,20 +74,14 @@ int svsm_perform_call_protocol(struct sv
 
 	flags = native_local_irq_save();
 
-	if (sev_cfg.ghcbs_initialized)
-		ghcb = __sev_get_ghcb(&state);
-	else if (boot_ghcb)
-		ghcb = boot_ghcb;
-	else
-		ghcb = NULL;
+	ghcb = __sev_get_ghcb(&state);
 
 	do {
 		ret = ghcb ? svsm_perform_ghcb_protocol(ghcb, call)
 			   : __pi_svsm_perform_msr_protocol(call);
 	} while (ret == -EAGAIN);
 
-	if (sev_cfg.ghcbs_initialized)
-		__sev_put_ghcb(&state);
+	__sev_put_ghcb(&state);
 
 	native_local_irq_restore(flags);
 
author maintainer
#381 lore raw

PATCH 381/395 x86/sev: Make vTPM SVSM calls preemption-safe

In reply to Greg Kroah-Hartman

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

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

From: Melody Wang <[email protected]>

[ Upstream commit 6c43c72748fffd29dec15cd1f31e9a32949bc437 ]

Two functions in the SVSM vTPM guest implementation do not disable preemption when fetching the SVSM Calling Area Address (CAA).

The SVSM CAA is a per-CPU structure. When a thread is preempted and migrated to a different CPU after fetching the per-CPU CAA, the SVSM call will execute on the new CPU with the original CPU's CAA. Which is wrong.

Move the CAA fetching operation inside svsm_perform_call_protocol() which disables interrupts around the SVSM call and thus runs preemption-safe.

---
 arch/x86/coco/sev/svsm.c |   10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)
arch/x86/coco/sev/svsm.c +8 -2
@@ -74,6 +74,14 @@ int svsm_perform_call_protocol(struct sv
 
 	flags = native_local_irq_save();
 
+	/*
+	 * 'caa' is a per-CPU variable. To avoid using a stale or incorrect
+	 * 'caa' if the task is preempted or migrated to another CPU after it
+	 * is fetched, always fetch 'caa' and then issue the SVSM call with
+	 * interrupts disabled. This ensures the correct 'caa' is used.
+	 */
+	call->caa = svsm_get_caa();
+
 	ghcb = __sev_get_ghcb(&state);
 
 	do {
@@ -321,7 +329,6 @@ int snp_svsm_vtpm_send_command(u8 *buffe
 {
 	struct svsm_call call = {};
 
-	call.caa = svsm_get_caa();
 	call.rax = SVSM_VTPM_CALL(SVSM_VTPM_CMD);
 	call.rcx = __pa(buffer);
 
@@ -345,7 +352,6 @@ bool snp_svsm_vtpm_probe(void)
 	if (!snp_vmpl)
 		return false;
 
-	call.caa = svsm_get_caa();
 	call.rax = SVSM_VTPM_CALL(SVSM_VTPM_QUERY);
 
 	if (svsm_perform_call_protocol(&call))
author maintainer
#382 lore raw

PATCH 382/395 net/sched: act_ct: fix helper UAF due to extensions realloc

In reply to Greg Kroah-Hartman

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

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

From: Ilya Maximets <[email protected]>

[ Upstream commit dad19b59da050cb60d3f7023dac2a042a84bf0bd ]

While calling the helpers, a raw pointer to the extensions area is wired into expectations list:

-> nf_ct_helper()
-> helper->help()

    -> nf_ct_expect_related_report()
     -> nf_ct_expect_insert()
      -> hlist_add_head_rcu(&exp->lnode, &master_help->expectations)

In case the connection is not confirmed yet, more extensions can be added afterwards with *_ext_add() calls reallocating the extension space and leaving the now invalid pointer in the expectations list that is later accessed while removing the expectation.

Make sure that helpers are called at the end after all the other extensions are already added.

Note that the helper rejection now leaves the mark and labels set, but that's not different from how the NAT was handled before or how the mark and the labels were handled on confirmation failure. And there are no atomicity guarantees provided by the API anyway.

Fixes a21b06e73191 ("net: sched: add helper support in act_ct")
[ Preserved the existing add_helper condition that upstream had removed. ]
---
 net/sched/act_ct.c |   18 ++++++++++++------
 1 file changed, 12 insertions(+), 6 deletions(-)
net/sched/act_ct.c +12 -6
@@ -1087,19 +1087,25 @@ do_nat:
 		}
 	}
 
-	if (nf_ct_is_confirmed(ct) ? ((!cached && !skip_add) || add_helper) : commit) {
-		err = nf_ct_helper(skb, ct, ctinfo, family);
-		if (err != NF_ACCEPT)
-			goto nf_error;
-	}
-
 	if (commit) {
 		tcf_ct_act_set_mark(ct, p->mark, p->mark_mask);
 		tcf_ct_act_set_labels(ct, p->labels, p->labels_mask);
 
 		if (!nf_ct_is_confirmed(ct))
 			nf_conn_act_ct_ext_add(skb, ct, ctinfo);
+	}
 
+	/* Run helpers for the connection if nf_conntrack_in() was executed
+	 * or if we're about to commit.  This has to be done after all the
+	 * extensions are already added.
+	 */
+	if (nf_ct_is_confirmed(ct) ? ((!cached && !skip_add) || add_helper) : commit) {
+		err = nf_ct_helper(skb, ct, ctinfo, family);
+		if (err != NF_ACCEPT)
+			goto nf_error;
+	}
+
+	if (commit) {
 		/* This will take care of sending queued events
 		 * even if the connection is already confirmed.
 		 */
author maintainer
#383 lore raw

PATCH 383/395 net/sched: act_ct: avoid modifying shared unconfirmed ct entry

In reply to Greg Kroah-Hartman

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

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

From: Ilya Maximets <[email protected]>

[ Upstream commit f85009dfcd65e5969526b0db7a49b5413746e630 ]

In a case where skb with an unconfirmed ct entry gets cloned, we may end up processing both again but with different sets of extensions.

The series of events:

1. The first clone wants to commit and runs the helpers wiring up the extension pointer into the expectation list.
2. Then it looses the confirmation keeping the entry unconfirmed.
3. Second clone now wants to commit labels or run NAT and adds the new extension for that breaking the pointer in the expectation list causing UAF on the destruction path later.

While this is possible to trigger, there should be no practical network pipeline where we need to process both clones without modifications in the same zone. So, let's just reset the entry in case for some reason we got an skb with a shared one. This doesn't affect any known use cases, but avoids any potential problems with sharing and modification of the unconfirmed ct entry.

Unlike openvswitch module, act_ct allows for NAT without commit. Changing that would be a uAPI break. So, act_ct needs to reset on NAT regardless of the commit flag to avoid reallocation of the extension space. This, however, doesn't really change the picture for sensible networking cases as there should be no need to run the same packet twice (before and after the clone) through conntrack without packet header or zone changes and without commit.

The fixes tag points to the introduction of helpers, since that's the main UAF trigger for the sharing.

---
 net/sched/act_ct.c |   18 ++++++++++++++++--
 1 file changed, 16 insertions(+), 2 deletions(-)
net/sched/act_ct.c +16 -2
@@ -977,11 +977,11 @@ TC_INDIRECT_SCOPE int tcf_ct_act(struct
 				 struct tcf_result *res)
 {
 	struct net *net = dev_net(skb->dev);
+	bool cached, commit, clear, nat;
 	enum ip_conntrack_info ctinfo;
 	struct tcf_ct *c = to_ct(a);
 	struct nf_conn *tmpl = NULL;
 	struct nf_hook_state state;
-	bool cached, commit, clear;
 	int nh_ofs, err, retval;
 	struct tcf_ct_params *p;
 	bool add_helper = false;
@@ -996,6 +996,7 @@ TC_INDIRECT_SCOPE int tcf_ct_act(struct
 	retval = p->action;
 	commit = p->ct_action & TCA_CT_ACT_COMMIT;
 	clear = p->ct_action & TCA_CT_ACT_CLEAR;
+	nat = p->ct_action & TCA_CT_ACT_NAT;
 	tmpl = p->tmpl;
 
 	tcf_lastuse_update(&c->tcf_tm);
@@ -1044,6 +1045,19 @@ TC_INDIRECT_SCOPE int tcf_ct_act(struct
 	 * different zone.
 	 */
 	cached = tcf_ct_skb_nfct_cached(net, skb, p);
+
+	/* If the ct entry is not confirmed and shared with some other skb,
+	 * e.g., a cloned one, we can't just modify it with a commit or nat
+	 * as we must not modify the extension set.  Reset.
+	 */
+	if (cached && (commit || nat)) {
+		ct = nf_ct_get(skb, &ctinfo);
+		if (ct && !nf_ct_is_confirmed(ct) && nf_ct_shared(ct)) {
+			nf_reset_ct(skb);
+			cached = false;
+		}
+	}
+
 	if (!cached) {
 		if (tcf_ct_flow_table_lookup(p, skb, family)) {
 			skip_add = true;
@@ -1081,7 +1095,7 @@ do_nat:
 		if (err)
 			goto drop;
 		add_helper = true;
-		if (p->ct_action & TCA_CT_ACT_NAT && !nfct_seqadj(ct)) {
+		if (nat && !nfct_seqadj(ct)) {
 			if (!nfct_seqadj_ext_add(ct))
 				goto drop;
 		}
author maintainer
#384 lore raw

PATCH 384/395 RISC-V: KVM: Fix null pointer dereference in kvm_riscv_aia_imsic_rw_attr()

In reply to Greg Kroah-Hartman

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

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

From: Jiakai Xu <[email protected]>

[ Upstream commit aeb1d17d1af5924f7357d7204a293bd8fc06ea13 ]

Add a null pointer check for imsic_state before dereferencing it in kvm_riscv_aia_imsic_rw_attr(). While the function checks that the vcpu exists, it doesn't verify that the vcpu's imsic_state has been initialized, leading to a null pointer dereference when accessed.

The crash manifests as:

  Unable to handle kernel paging request at virtual address
  dfffffff00000006
  ...
  kvm_riscv_aia_imsic_rw_attr+0x2d8/0x854 arch/riscv/kvm/aia_imsic.c:958
  aia_set_attr+0x2ee/0x1726 arch/riscv/kvm/aia_device.c:354
  kvm_device_ioctl_attr virt/kvm/kvm_main.c:4744 [inline]
  kvm_device_ioctl+0x296/0x374 virt/kvm/kvm_main.c:4761
  vfs_ioctl fs/ioctl.c:51 [inline]
  ...

The fix adds a check to return -ENODEV if imsic_state is NULL and moves isel assignment after imsic_state NULL check.

Fixes 5463091a51cfaa ("RISC-V: KVM: Expose IMSIC registers as attributes of AIA irqchip")

Stable-dep-of: 8ae12ccaec6e ("RISC-V: KVM: Serialize IMSIC attributes with vCPU migration")

---
 arch/riscv/kvm/aia_imsic.c |    4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
arch/riscv/kvm/aia_imsic.c +3 -1
@@ -952,8 +952,10 @@ int kvm_riscv_aia_imsic_rw_attr(struct k
 	if (!vcpu)
 		return -ENODEV;
 
-	isel = KVM_DEV_RISCV_AIA_IMSIC_GET_ISEL(type);
 	imsic = vcpu->arch.aia_context.imsic_state;
+	if (!imsic)
+		return -ENODEV;
+	isel = KVM_DEV_RISCV_AIA_IMSIC_GET_ISEL(type);
 
 	read_lock_irqsave(&imsic->vsfile_lock, flags);
 
author maintainer
#385 lore raw

PATCH 385/395 RISC-V: KVM: Serialize IMSIC attributes with vCPU migration

In reply to Greg Kroah-Hartman

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

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

From: Xie Bo <[email protected]>

[ Upstream commit 8ae12ccaec6ec74945d8c1ef39f2c1b8df779abc ]

KVM device ioctls are not serialized against KVM_RUN. As a result, kvm_riscv_aia_imsic_rw_attr() can snapshot the physical CPU and HGEI of an IMSIC VS-file before a concurrent vCPU migration releases it.

The HGEI can then be allocated to another vCPU before imsic_vsfile_rw() uses the stale tuple. A GET or SET attribute may consequently access the new owner's interrupt file.

Serialize the entire IMSIC attribute operation with the target vCPU mutex. This prevents the VS-file from being migrated and recycled until the attribute access completes. Acquire the mutex killably so that the device ioctl remains interruptible while waiting for KVM_RUN to finish.

Fixes db8b7e97d613 ("RISC-V: KVM: Add in-kernel virtualization of AIA IMSIC")
---
 arch/riscv/kvm/aia_imsic.c |   11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)
arch/riscv/kvm/aia_imsic.c +9 -2
@@ -952,9 +952,14 @@ int kvm_riscv_aia_imsic_rw_attr(struct k
 	if (!vcpu)
 		return -ENODEV;
 
+	if (mutex_lock_killable(&vcpu->mutex))
+		return -EINTR;
+
 	imsic = vcpu->arch.aia_context.imsic_state;
-	if (!imsic)
-		return -ENODEV;
+	if (!imsic) {
+		rc = -ENODEV;
+		goto out_unlock;
+	}
 	isel = KVM_DEV_RISCV_AIA_IMSIC_GET_ISEL(type);
 
 	read_lock_irqsave(&imsic->vsfile_lock, flags);
@@ -978,6 +983,8 @@ int kvm_riscv_aia_imsic_rw_attr(struct k
 		rc = imsic_vsfile_rw(vsfile_hgei, vsfile_cpu, imsic->nr_eix,
 				     isel, write, val);
 
+out_unlock:
+	mutex_unlock(&vcpu->mutex);
 	return rc;
 }
 
author maintainer
#386 lore raw

PATCH 386/395 Revert "rust: net: phy: fix off-by-one bit positions in device status accessors"

In reply to Greg Kroah-Hartman

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

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

From: Greg Kroah-Hartman <[email protected]>

This reverts commit f22d349940cb34e56f6e8cd86d69c36d3998b33a which is commit 6fb0a9d9071f1ff0cc5cfc0782302d9c90d642cb upstream.

It was not meant to be backported before 7.1.y -- its tag said:

Cc [email protected] # Only 7.1.y and later (requires bindgen's raw pointer accessors).
---
 rust/kernel/net/phy.rs |   38 ++++++++++++++++++++------------------
 1 file changed, 20 insertions(+), 18 deletions(-)
rust/kernel/net/phy.rs +20 -18
@@ -123,37 +123,39 @@ impl Device {
     /// Gets the current link state.
     ///
     /// It returns true if the link is up.
-    #[inline]
     pub fn is_link_up(&self) -> bool {
-        let phydev = self.0.get().cast_const();
-        // SAFETY: By the type invariant of `Device`, `phydev` points to a valid
-        // `struct phy_device`, and there is no concurrent write to this field.
-        let link = unsafe { bindings::phy_device::link_raw(phydev) };
-        link == 1
+        const LINK_IS_UP: u64 = 1;
+        // TODO: the code to access to the bit field will be replaced with automatically
+        // generated code by bindgen when it becomes possible.
+        // SAFETY: The struct invariant ensures that we may access
+        // this field without additional synchronization.
+        let bit_field = unsafe { &(*self.0.get())._bitfield_1 };
+        bit_field.get(14, 1) == LINK_IS_UP
     }
 
     /// Gets the current auto-negotiation configuration.
     ///
     /// It returns true if auto-negotiation is enabled.
-    #[inline]
     pub fn is_autoneg_enabled(&self) -> bool {
-        let phydev = self.0.get().cast_const();
-        // SAFETY: By the type invariant of `Device`, `phydev` points to a valid
-        // `struct phy_device`, and there is no concurrent write to this field.
-        let autoneg = unsafe { bindings::phy_device::autoneg_raw(phydev) };
-        autoneg == bindings::AUTONEG_ENABLE
+        // TODO: the code to access to the bit field will be replaced with automatically
+        // generated code by bindgen when it becomes possible.
+        // SAFETY: The struct invariant ensures that we may access
+        // this field without additional synchronization.
+        let bit_field = unsafe { &(*self.0.get())._bitfield_1 };
+        bit_field.get(13, 1) == u64::from(bindings::AUTONEG_ENABLE)
     }
 
     /// Gets the current auto-negotiation state.
     ///
     /// It returns true if auto-negotiation is completed.
-    #[inline]
     pub fn is_autoneg_completed(&self) -> bool {
-        let phydev = self.0.get().cast_const();
-        // SAFETY: By the type invariant of `Device`, `phydev` points to a valid
-        // `struct phy_device`, and there is no concurrent write to this field.
-        let completed = unsafe { bindings::phy_device::autoneg_complete_raw(phydev) };
-        completed == 1
+        const AUTONEG_COMPLETED: u64 = 1;
+        // TODO: the code to access to the bit field will be replaced with automatically
+        // generated code by bindgen when it becomes possible.
+        // SAFETY: The struct invariant ensures that we may access
+        // this field without additional synchronization.
+        let bit_field = unsafe { &(*self.0.get())._bitfield_1 };
+        bit_field.get(15, 1) == AUTONEG_COMPLETED
     }
 
     /// Sets the speed of the PHY.
author maintainer
#387 lore raw

PATCH 387/395 rust: net: phy: fix off-by-one bit positions in device status accessors

In reply to Greg Kroah-Hartman

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

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

From: Chunfeng Song <[email protected]>

commit 6fb0a9d9071f1ff0cc5cfc0782302d9c90d642cb upstream.

The hand-written bitfield offsets in is_link_up(), is_autoneg_enabled() and is_autoneg_completed() were correct when the abstraction was
merged: at that time autoneg, link, and autoneg_complete were at bits 13, 14, and 15 of struct phy_device's first bitfield unit. Commit 2796ff1e3dca ("net: phy: add flag is_genphy_driven to struct phy_device") later inserted is_genphy_driven just before autoneg, shifting the three fields up by one, so the accessors now read:

is_link_up() reads bit 14 = autoneg
is_autoneg_enabled() reads bit 13 = is_genphy_driven
is_autoneg_completed() reads bit 15 = link

The official ax88796b Rust driver uses all three accessors in its read_status() implementation, so it inherits the bug. phy_attach_direct() sets is_genphy_driven only when it falls back to the generic driver, and ax88796b has a real driver, so is_genphy_driven stays 0. The broken is_autoneg_enabled() therefore reads bit 13 as 0, compares it against AUTONEG_ENABLE (1), and always returns false, so read_status() never reaches the resolve_aneg_linkmode() call.

The ordinary bindgen accessors take &self. Calling them through (*phydev).link() would create a shared reference to the complete bindings::phy_device, which is not appropriate for an object wrapped in Opaque.

Use the bindgen-generated raw accessors (link_raw(), autoneg_raw(), and autoneg_complete_raw()) instead. They retain the bit positions and endianness handling generated from the C layout without creating a Rust reference to the complete phy_device. Drop the hand-written numbers together with the TODO comment that marked them as a stopgap.

The raw accessors are only emitted by bindgen 0.71 and later, and were added at the Rust-for-Linux project's request, so this fix can only be backported to stable branches whose minimum bindgen version is at least that, hence the scope on the Cc: stable line below.

Found by a static equivalence audit (C2RustDrv, a C-to-Rust driver migration tool) that compares hand-written bitfield offsets against the bindgen layout of struct phy_device. Verified by building the bindings and checking the generated accessors; no runtime testing was possible without PHY hardware.

Fixes 2796ff1e3dca ("net: phy: add flag is_genphy_driven to struct phy_device")
Cc [email protected] # Only 7.1.y and later (requires bindgen's raw pointer accessors).

[ For 6.18.y, re-do the commit manually adjusting the bit numbers to
make it work with the bindgen version available there as Chunfeng mentions in [1]. - Miguel ]

---
 rust/kernel/net/phy.rs |    6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)
rust/kernel/net/phy.rs +3 -3
@@ -130,7 +130,7 @@ impl Device {
         // SAFETY: The struct invariant ensures that we may access
         // this field without additional synchronization.
         let bit_field = unsafe { &(*self.0.get())._bitfield_1 };
-        bit_field.get(14, 1) == LINK_IS_UP
+        bit_field.get(15, 1) == LINK_IS_UP
     }
 
     /// Gets the current auto-negotiation configuration.
@@ -142,7 +142,7 @@ impl Device {
         // SAFETY: The struct invariant ensures that we may access
         // this field without additional synchronization.
         let bit_field = unsafe { &(*self.0.get())._bitfield_1 };
-        bit_field.get(13, 1) == u64::from(bindings::AUTONEG_ENABLE)
+        bit_field.get(14, 1) == u64::from(bindings::AUTONEG_ENABLE)
     }
 
     /// Gets the current auto-negotiation state.
@@ -155,7 +155,7 @@ impl Device {
         // SAFETY: The struct invariant ensures that we may access
         // this field without additional synchronization.
         let bit_field = unsafe { &(*self.0.get())._bitfield_1 };
-        bit_field.get(15, 1) == AUTONEG_COMPLETED
+        bit_field.get(16, 1) == AUTONEG_COMPLETED
     }
 
     /// Sets the speed of the PHY.
author maintainer
#388 lore raw

PATCH 388/395 mm/damon/core: fix unconditionally skip last region

In reply to Greg Kroah-Hartman

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

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

From: Liew Rui Yan <[email protected]>

commit b3723b596b548c837a766aae3553c14a7b15af2b upstream.

Once quota set, the charge_{target,addr}_from unconditionally skips and resets at the last region of the tracked target, so the last region can be skipped even when it has not been processed.

Example:

1. Target has 2 regions: R1 (0-100 bytes) and R2 (100-200 bytes).
2. Quota is configured to process only 100 bytes per window.
3. Window 1: Processes R1 (0-100). Quota is full. charge_{target, addr}_from is saved at (Target, 100).
4. Window 2: The loop reaches R2. Because R2 is damon_last_region(t), the old code unconditionally returns true, skipping R2 entirely and resetting the charge_{target,addr}_from.

Result: R2 is permanently skipped even though it has never been processed.

However, it is important to note that this is a very minor issue. This is because it is triggered only when the previous window saved/kept charge_{target,addr}_from, and in the next window, all regions except the last region were skipped by damos_skip_charged_region().

Fix this by only resetting the charge_{target,addr}_from when last region is reached, only skipping when it is applied or cannot split.

Fixes 50585192bc2e ("mm/damon/schemes: skip already charged targets and regions")
Cc <[email protected]> # v5.16.x
---
 mm/damon/core.c |   24 ++++++++++++++----------
 1 file changed, 14 insertions(+), 10 deletions(-)
mm/damon/core.c +14 -10
@@ -1746,37 +1746,41 @@ static bool damos_skip_charged_region(st
 	struct damon_region *r = *rp;
 	struct damos_quota *quota = &s->quota;
 	unsigned long sz_to_skip;
+	bool skip = false;
 
 	/* Skip previously charged regions */
 	if (quota->charge_target_from) {
 		if (t != quota->charge_target_from)
 			return true;
-		if (r == damon_last_region(t)) {
-			quota->charge_target_from = NULL;
-			quota->charge_addr_from = 0;
-			return true;
-		}
 		if (quota->charge_addr_from &&
-				r->ar.end <= quota->charge_addr_from)
-			return true;
+				r->ar.end <= quota->charge_addr_from) {
+			skip = true;
+			goto out;
+		}
 
 		if (quota->charge_addr_from && r->ar.start <
 				quota->charge_addr_from) {
 			sz_to_skip = ALIGN_DOWN(quota->charge_addr_from -
 					r->ar.start, min_sz_region);
 			if (!sz_to_skip) {
-				if (damon_sz_region(r) <= min_sz_region)
-					return true;
+				if (damon_sz_region(r) <= min_sz_region) {
+					skip = true;
+					goto out;
+				}
 				sz_to_skip = min_sz_region;
 			}
 			damon_split_region_at(t, r, sz_to_skip);
 			r = damon_next_region(r);
 			*rp = r;
+			skip = false;
 		}
+	}
+out:
+	if (r == damon_last_region(t)) {
 		quota->charge_target_from = NULL;
 		quota->charge_addr_from = 0;
 	}
-	return false;
+	return skip;
 }
 
 static void damos_update_stat(struct damos *s,
author maintainer
#389 lore raw

PATCH 389/395 hfsplus: avoid double unload_nls() on mount failure

In reply to Greg Kroah-Hartman

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

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

From: Shardul Bankar <[email protected]>

commit ebebb04baefdace1e0dc17f7779e5549063ca592 upstream.

The recent commit "hfsplus: ensure sb->s_fs_info is always cleaned up"
[1] introduced a custom ->kill_sb() handler (hfsplus_kill_super) that cleans up the s_fs_info structure (including the NLS table) on superblock destruction.

However, the error handling path in hfsplus_fill_super() still calls unload_nls() before returning an error. Since the VFS layer calls ->kill_sb() when fill_super fails, this results in unload_nls() being called twice for the same sbi->nls pointer: once in hfsplus_fill_super() and again in hfsplus_kill_super() (via delayed_free).

Remove the explicit unload_nls() call from the error path in hfsplus_fill_super() to rely solely on the cleanup in ->kill_sb().

[1] https://lore.kernel.org/r/[email protected]/

---

---
 fs/hfsplus/super.c |    1 -
 1 file changed, 1 deletion(-)
fs/hfsplus/super.c +0 -1
@@ -652,7 +652,6 @@ out_free_vhdr:
 	kfree(sbi->s_vhdr_buf);
 	kfree(sbi->s_backup_vhdr_buf);
 out_unload_nls:
-	unload_nls(sbi->nls);
 	unload_nls(nls);
 	return err;
 }
author maintainer
#390 lore raw

PATCH 390/395 sched_ext: Derive SCX_RQ_IN_WAKEUP from the core enqueue flags

In reply to Greg Kroah-Hartman

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

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

From: Tejun Heo <[email protected]>

[ Upstream commit df5cdc2c832ca4e8a6d774596b9005558761a403 ]

schedule_deferred_locked() skips scheduling a deferred action while SCX_RQ_IN_WAKEUP is set and relies on the task_woken_scx() call that follows a wakeup enqueue to run it. enqueue_task_scx() sets the flag from the merged enqueue flags, which include the flags stashed for a remote activation. move_remote_task_to_local_dsq() thus sets SCX_RQ_IN_WAKEUP on the destination rq when the moved task was woken up, although no task_woken_scx() follows that activation.

An IMMED insert into a busy destination requests a local reenqueue during that enqueue. The request gets linked but not scheduled and stays pending until an unrelated wakeup or preemption on that CPU runs the deferred actions. The IMMED task sits behind the running task in the meantime. If nothing runs them before the scheduler is disabled, the request outlives the scheduler and points into its freed per-cpu area, which the next scheduler dereferences from run_deferred().

Test the core enqueue flags for the wakeup bit. Only the core's wakeup path is followed by task_woken_scx().

Fixes 57ccf5ccdc56 ("sched_ext: Fix enqueue_task_scx() truncation of upper enqueue flags")
[ Retargeted the patch to kernel/sched/ext.c with its older extra_enq_flags context. ]
---
 kernel/sched/ext.c |    7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)
kernel/sched/ext.c +6 -1
@@ -1382,7 +1382,12 @@ static void enqueue_task_scx(struct rq *
 	int sticky_cpu = p->scx.sticky_cpu;
 	u64 enq_flags = core_enq_flags | rq->scx.extra_enq_flags;
 
-	if (enq_flags & ENQUEUE_WAKEUP)
+	/*
+	 * SCX_RQ_IN_WAKEUP promises a task_woken_scx() call once this enqueue
+	 * returns. Only the core's wakeup path delivers one. The flags stashed
+	 * for a remote activation may carry the wakeup bit without it.
+	 */
+	if (core_enq_flags & ENQUEUE_WAKEUP)
 		rq->scx.flags |= SCX_RQ_IN_WAKEUP;
 
 	if (sticky_cpu >= 0)
author maintainer
#391 lore raw

PATCH 391/395 rose: keep the route needed for routing loop detection

In reply to Greg Kroah-Hartman

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

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

From: Bernard Pidoux <[email protected]>

rose_route_frame() walks the route list before parsing the Call Request facilities. When a CALL REQUEST arrives on a LCI that already belongs to a route, the "Remove an existing unused route" branch deletes that route with rose_remove_route() and breaks out of the walk, so that a new call is able to reuse a stale LCI.

The routing loop detection that follows looks for a route recording the same random number and the same pair of callsigns as the incoming call. The route that has just been deleted is exactly the one it would have matched, so a call coming back to us around a routing loop is not
detected: it is routed again, a new route is allocated, and the next copy repeats the cycle. Two nodes whose routes point at each other then relay the same call to each other as fast as the CPU allows.

This was observed on an AX.25 network where two nodes each believed the route to a third one went through the other. The same CALL REQUEST, carrying the same random number, was re-sent every 400 us, interleaved with CLEAR REQUEST "Not obtainable", diagnostic 120, until the AX.25 link collapsed. Over nine days one node emitted 204 million frames on a link that normally carries a few frames per minute. The outgoing LCI stayed at 001 the whole time, because deleting the route freed it for immediate reuse.

Parse the facilities before walking the route list, and in both branches remove the route only when the incoming call differs from the one it records. When it is the same call, keep the route and clear the call with diagnostic 120, which is what the loop detection would have done. The predicate is factored out as rose_same_call() and reused by the loop detection itself.

Tested with two ROSE nodes routing a call back to each other. Before this change the kernel relayed 377 CALL REQUEST in ten seconds, never emitted a CLEAR, and the AX.25 link dropped. After it, the first call is relayed once, the next copy is cleared with diagnostic 120, and the link stays up.

---
 net/rose/rose_route.c |   71 +++++++++++++++++++++++++++++++++++++++-----------
 1 file changed, 56 insertions(+), 15 deletions(-)
net/rose/rose_route.c +56 -15
@@ -852,6 +852,21 @@ void rose_link_device_down(struct net_de
 }
 
 /*
+ *	Is this Call Request the very same call as the one an existing route
+ *	was built for?  Both the loop detection and the reuse of a stale LCI
+ *	below rely on this test.
+ */
+static bool rose_same_call(const struct rose_route *rose_route,
+			   const rose_address *src_addr,
+			   const struct rose_facilities_struct *facilities)
+{
+	return rose_route->rand == facilities->rand &&
+	       rosecmp(src_addr, &rose_route->src_addr) == 0 &&
+	       ax25cmp(&facilities->dest_call, &rose_route->src_call) == 0 &&
+	       ax25cmp(&facilities->source_call, &rose_route->dest_call) == 0;
+}
+
+/*
  *	Route a frame to an appropriate AX.25 connection.
  *	A NULL ax25_cb indicates an internally generated frame.
  */
@@ -961,6 +976,16 @@ int rose_route_frame(struct sk_buff *skb
 		goto out;
 	}
 
+	memset(&facilities, 0x00, sizeof(struct rose_facilities_struct));
+
+	if (frametype == ROSE_CALL_REQUEST &&
+	    !rose_parse_facilities(skb->data + ROSE_CALL_REQ_FACILITIES_OFF,
+				   skb->len - ROSE_CALL_REQ_FACILITIES_OFF,
+				   &facilities)) {
+		rose_transmit_clear_request(rose_neigh, lci, ROSE_INVALID_FACILITY, 76);
+		goto out;
+	}
+
 	/*
 	 *	Route it to the next in line if we have an entry for it.
 	 */
@@ -969,7 +994,21 @@ int rose_route_frame(struct sk_buff *skb
 		if (rose_route->lci1 == lci &&
 		    rose_route->neigh1 == rose_neigh) {
 			if (frametype == ROSE_CALL_REQUEST) {
-				/* F6FBB - Remove an existing unused route */
+				if (rose_same_call(rose_route, src_addr,
+						   &facilities)) {
+					/*
+					 *	The very same call coming back
+					 *	to us: a routing loop.  Keep
+					 *	the route, the loop detection
+					 *	below is what matches on it.
+					 */
+					rose_transmit_clear_request(rose_neigh,
+								    lci,
+								    ROSE_NOT_OBTAINABLE,
+								    120);
+					goto out;
+				}
+				/* Remove an existing unused route */
 				rose_remove_route(rose_route);
 				break;
 			} else if (rose_route->neigh2 != NULL) {
@@ -990,7 +1029,21 @@ int rose_route_frame(struct sk_buff *skb
 		if (rose_route->lci2 == lci &&
 		    rose_route->neigh2 == rose_neigh) {
 			if (frametype == ROSE_CALL_REQUEST) {
-				/* F6FBB - Remove an existing unused route */
+				if (rose_same_call(rose_route, src_addr,
+						   &facilities)) {
+					/*
+					 *	The very same call coming back
+					 *	to us: a routing loop.  Keep
+					 *	the route, the loop detection
+					 *	below is what matches on it.
+					 */
+					rose_transmit_clear_request(rose_neigh,
+								    lci,
+								    ROSE_NOT_OBTAINABLE,
+								    120);
+					goto out;
+				}
+				/* Remove an existing unused route */
 				rose_remove_route(rose_route);
 				break;
 			} else if (rose_route->neigh1 != NULL) {
@@ -1021,24 +1074,12 @@ int rose_route_frame(struct sk_buff *skb
 		goto out;
 	}
 
-	memset(&facilities, 0x00, sizeof(struct rose_facilities_struct));
-
-	if (!rose_parse_facilities(skb->data + ROSE_CALL_REQ_FACILITIES_OFF,
-				   skb->len - ROSE_CALL_REQ_FACILITIES_OFF,
-				   &facilities)) {
-		rose_transmit_clear_request(rose_neigh, lci, ROSE_INVALID_FACILITY, 76);
-		goto out;
-	}
-
 	/*
 	 *	Check for routing loops.
 	 */
 	rose_route = rose_route_list;
 	while (rose_route != NULL) {
-		if (rose_route->rand == facilities.rand &&
-		    rosecmp(src_addr, &rose_route->src_addr) == 0 &&
-		    ax25cmp(&facilities.dest_call, &rose_route->src_call) == 0 &&
-		    ax25cmp(&facilities.source_call, &rose_route->dest_call) == 0) {
+		if (rose_same_call(rose_route, src_addr, &facilities)) {
 			rose_transmit_clear_request(rose_neigh, lci, ROSE_NOT_OBTAINABLE, 120);
 			goto out;
 		}
author maintainer
#392 lore raw

PATCH 392/395 rose: guard rose_transmit_link() against a NULL neighbour

In reply to Greg Kroah-Hartman

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

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

From: Bernard Pidoux <[email protected]>

rose_kick() and rose_write_internal() call rose_transmit_link(skb, rose->neighbour) without checking that rose->neighbour is still set. Since commit e8eb0c6faa88 ("rose: clear neighbour pointer after rose_neigh_put() in state machines"), the state machines routinely set rose->neighbour to NULL once the underlying AX.25 link is gone, while leaving the socket in ROSE_STATE_3. rose_kick() only checks the state, not the neighbour, so a write() on such a socket reaches rose_transmit_link() with neigh == NULL, which immediately dereferences neigh->loopback and crashes:

  Unable to handle kernel NULL pointer dereference at
  virtual address 0000000000000036
  ...
  pc : rose_transmit_link+0x14/0x1c8 [rose]
  lr : rose_kick+0xec/0x198 [rose]
  Call trace:
   rose_transmit_link+0x14/0x1c8 [rose] (P)
   rose_kick+0xec/0x198 [rose]
   rose_sendmsg+0x260/0x3c8 [rose]

Reproduced on f6bvp-8 (a real ROSE/FPAC node) when the BBS daemon wrote to a socket whose neighbour had just been cleared. Confirmed by recompiling rose_link.o with a static_assert on offsetof(struct rose_neigh, loopback), which matches the faulting address (0x36) exactly.

Drop the skb and return early when neigh is NULL, matching what rose_transmit_link() already does for other frames it cannot send.

Fixes e8eb0c6faa88 ("rose: clear neighbour pointer after rose_neigh_put() in state machines")
---
 net/rose/rose_link.c |    5 +++++
 1 file changed, 5 insertions(+)
net/rose/rose_link.c +5 -0
@@ -263,6 +263,11 @@ void rose_transmit_link(struct sk_buff *
 {
 	unsigned char *dptr;
 
+	if (!neigh) {
+		kfree_skb(skb);
+		return;
+	}
+
 	if (neigh->loopback) {
 		rose_loopback_queue(skb, neigh);
 		return;
author maintainer
#393 lore raw

PATCH 393/395 rose: use timer_shutdown_sync() for t0timer teardown

In reply to Greg Kroah-Hartman

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

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

From: Bernard Pidoux <[email protected]>

rose_t0timer_expiry() re-arms itself via rose_start_t0timer() at its own tail. rose_neigh_put() and rose_remove_neigh() stop it with timer_delete_sync() before freeing (or unlinking) the neighbour, but that only guarantees the callback is not running *at the moment the call returns* -- it does nothing to stop the very invocation that was just waited out from re-arming the timer on its way out. That re-arm races the kfree() in rose_neigh_put(): the timer can fire again on freed memory, and rose_t0timer_expiry() -> rose_transmit_restart_request() -> rose_send_frame() -> ax25_send_frame() dereferences the freed neigh->digipeat, use-after-free.

This matches a syzbot report (KASAN slab-use-after-free read in ax25_find_cb()) that stayed open with both cause and fix bisection failing -- consistent with a hole that timer_delete_sync() alone cannot close for a self-rearming timer, a case documented in its own kerneldoc ("there is no way to get this correct with timer_delete_sync()").

Use timer_shutdown_sync() instead in both call sites. Unlike timer_delete_sync(), it also marks the timer so that any further add_timer()/mod_timer() on it is silently ignored, which closes the window regardless of how the self-rearm and the free are interleaved. ftimer's handler is a no-op and never re-arms, but it is switched the same way for consistency: both timers are being torn down for good in both of these call sites.

---
 include/net/rose.h    |   28 ++++++++++++++++++----------
 net/rose/rose_route.c |   10 ++++++++--
 2 files changed, 26 insertions(+), 12 deletions(-)
include/net/rose.h +18 -10
@@ -161,17 +161,25 @@ static inline void rose_neigh_put(struct
 {
 	if (refcount_dec_and_test(&rose_neigh->use)) {
 		/* We are dropping the last reference, so we are about to free the
-		 * neighbour.  Its timers may still be armed -- t0timer in particular
-		 * re-arms itself in rose_t0timer_expiry().  rose_remove_neigh()
-		 * cancels them before its own put, but callers that drop the final
-		 * reference without first calling rose_remove_neigh() (the socket
-		 * heartbeat reaping path) would otherwise kfree() a neighbour with a
-		 * live timer -> use-after-free.  timer_delete_sync() (not the async
-		 * variant) is required: it waits out a concurrently running handler
-		 * and loops until the self-rearming timer stays stopped.
+		 * neighbour.  t0timer is self-rearming: rose_t0timer_expiry() calls
+		 * rose_start_t0timer() at its own tail, so a plain timer_delete_sync()
+		 * is not enough here.  It only guarantees that the callback is not
+		 * running *at the moment it returns* -- it does nothing to stop the
+		 * very invocation we just waited out from re-arming the timer on its
+		 * way out, which races the kfree() below (syzbot: use-after-free read
+		 * in ax25_find_cb(), reached via rose_t0timer_expiry() ->
+		 * rose_transmit_restart_request() -> rose_send_frame() ->
+		 * ax25_send_frame(), dereferencing the freed neigh->digipeat).
+		 * timer_shutdown_sync() closes that hole: once it returns, any
+		 * further add_timer()/mod_timer() on this timer is silently ignored,
+		 * so a self-rearm racing the free can no longer bring the timer back
+		 * to life on freed memory.  ftimer's handler is a no-op and never
+		 * re-arms, but it is shut down the same way here for consistency --
+		 * this is final teardown, neither timer has any business firing
+		 * again.
 		 */
-		timer_delete_sync(&rose_neigh->ftimer);
-		timer_delete_sync(&rose_neigh->t0timer);
+		timer_shutdown_sync(&rose_neigh->ftimer);
+		timer_shutdown_sync(&rose_neigh->t0timer);
 		if (rose_neigh->ax25)
 			ax25_cb_put(rose_neigh->ax25);
 		kfree(rose_neigh->digipeat);
net/rose/rose_route.c +8 -2
@@ -229,8 +229,14 @@ static void rose_remove_neigh(struct ros
 {
 	struct rose_neigh *s;
 
-	timer_delete_sync(&rose_neigh->ftimer);
-	timer_delete_sync(&rose_neigh->t0timer);
+	/* Being removed from the list is final teardown for this neighbour:
+	 * shut the timers down rather than just deleting them, so a t0timer
+	 * self-rearm losing the race with this removal can't bring it back to
+	 * life later.  See the comment in rose_neigh_put() (include/net/rose.h)
+	 * for the full story.
+	 */
+	timer_shutdown_sync(&rose_neigh->ftimer);
+	timer_shutdown_sync(&rose_neigh->t0timer);
 
 	skb_queue_purge(&rose_neigh->queue);
 
author maintainer
#394 lore raw

PATCH 394/395 rose: dont warn on stray CALL_ACCEPTED/CLEAR_CONFIRMATION in state 3

In reply to Greg Kroah-Hartman

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

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

From: Bernard Pidoux <[email protected]>

rose_state3_machine() logs "ROSE: unknown %02X in state 3" at KERN_WARNING for any frame type it does not handle during data transfer. Two of them reach that default arm routinely on real AX.25 links and alarm sysops watching the console, even though they are harmless:

- CALL_ACCEPTED (0x0F): a late or duplicated Call Accepted that arrives after the socket has already moved to STATE_3, typically a retransmission on a slow link.

- CLEAR_CONFIRMATION (0x17): crossed clearing, or a clear confirmation left over from a previous incarnation of a reused logical channel.

In both cases the frame is simply dropped: no state change, no teardown, nothing freed. Only the noise is a problem.

Drop these two frame types silently. Keep reporting any other, genuinely unexpected frame type, but through net_warn_ratelimited() so a misbehaving peer cannot flood the kernel log.

---
 net/rose/rose_in.c |   13 ++++++++++++-
 1 file changed, 12 insertions(+), 1 deletion(-)
net/rose/rose_in.c +12 -1
@@ -203,7 +203,18 @@ static int rose_state3_machine(struct so
 		break;
 
 	default:
-		printk(KERN_WARNING "ROSE: unknown %02X in state 3\n", frametype);
+		/*
+		 * CALL_ACCEPTED (0x0F) and CLEAR_CONFIRMATION (0x17) may show
+		 * up in state 3 as a late or duplicated Call Accepted, or as
+		 * crossed clearing / a reused logical channel on a slow AX.25
+		 * link. These are harmless protocol races, so drop them
+		 * silently. Any other frame type is genuinely unexpected and
+		 * is still reported, rate limited to avoid flooding the log.
+		 */
+		if (frametype != ROSE_CALL_ACCEPTED &&
+		    frametype != ROSE_CLEAR_CONFIRMATION)
+			net_warn_ratelimited("ROSE: unknown %02X in state 3\n",
+					     frametype);
 		break;
 	}
 
author maintainer
#395 lore raw

PATCH 395/395 bpf: Reject key-less BTF for hash maps

In reply to Greg Kroah-Hartman

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

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

From: Jiayuan Chen <[email protected]>

commit 0895a0c0734703be5532f3883c42db95615fd98b upstream.

map_check_btf() allows a key-less BTF (btf_key_type_id == 0) only for maps that have a ->map_check_btf callback, and leaves the actual decision to that callback. Hash maps used to have no ->map_check_btf, so a key-less BTF was rejected outright.

That changed when htab and rhtab gained a ->map_check_btf to register a dtor - htab in commit 1df97a7453ee ("bpf: Register dtor for freeing special fields") and rhtab in commit 6905f8601298 ("bpf: Allow special fields in resizable hashtab"). Neither looks at the key, so a key-less hash map now passes map_check_btf() and gets created. Reading it back through bpffs feeds the key type_id 0 into btf_type_seq_show(); btf_type_by_id() returns the void type, kind_ops[BTF_KIND_UNKN] is NULL, and btf_type_show() dereferences it:

RIP: 0010:btf_type_show+0x223/0x2e0 kernel/bpf/btf.c:8232
RSP: 0018:ffffc9000399f868 EFLAGS: 00010206
RAX: dffffc0000000000 RBX: 0000000000000000 RCX: 0000000000000000
RDX: 0000000000000005 RSI: 0000000000000000 RDI: 0000000000000028
RBP: 0000000000000000 R08: 0000000000000001 R09: 0000000000000000
R10: ffffc9000399f970 R11: 0000000000000001 R12: ffffffff9b96b140
R13: ffffc9000399f8e0 R14: ffff88803d393c00 R15: 0000000000000003
CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 0000200000000000 CR3: 000000003d213000 CR4: 0000000000352ef0
DR0: 0000000039ae8f55 DR1: 0000000000000000 DR2: 0000000000000000
DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400
Call Trace:
 <TASK>
 btf_type_seq_show_flags+0xca/0x120 kernel/bpf/btf.c:8250
 htab_map_seq_show_elem+0x12e/0x350 kernel/bpf/hashtab.c:1669
 map_seq_show+0x13d/0x1e0 kernel/bpf/inode.c:293
 traverse.part.0.constprop.0+0x107/0x650 fs/seq_file.c:112
 traverse fs/seq_file.c:99 [inline]
 seq_read_iter+0x93f/0x1270 fs/seq_file.c:196
 seq_read+0x344/0x4d0 fs/seq_file.c:163
 vfs_read+0x1e4/0xb40 fs/read_write.c:572
 ksys_pread64 fs/read_write.c:764 [inline]
 __do_sys_pread64 fs/read_write.c:772 [inline]
 __se_sys_pread64 fs/read_write.c:769 [inline]
 __x64_sys_pread64+0x1eb/0x250 fs/read_write.c:769
 do_syscall_x64 arch/x86/entry/syscall_64.c:61 [inline]
 do_syscall_64+0x123/0x790 arch/x86/entry/syscall_64.c:84
 entry_SYSCALL_64_after_hwframe+0x77/0x7f

Reject a key-less BTF in htab_map_check_btf() and rhtab_map_check_btf(), restoring the previous behavior.

Fixes 1df97a7453ee ("bpf: Register dtor for freeing special fields")
Fixes 6905f8601298 ("bpf: Allow special fields in resizable hashtab")
Reported-by [email protected]
---
 kernel/bpf/hashtab.c |    3 +++
 1 file changed, 3 insertions(+)
kernel/bpf/hashtab.c +3 -0
@@ -543,6 +543,9 @@ static int htab_map_check_btf(const stru
 {
 	struct bpf_htab *htab = container_of(map, struct bpf_htab, map);
 
+	if (btf_type_is_void(key_type))
+		return -EINVAL;
+
 	if (htab_is_prealloc(htab))
 		return 0;
 	/*
Tested-by on the v1 series
#396 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 6.18.55 release. There are 395 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 Fri, 02 Oct 2026 15:23:04 +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/v6.x/stable-review/patch-6.18.55-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-6.18.y
and the diffstat can be found below.

thanks,

greg k-h

On ARCH_BRCMSTB using 32-bit and 64-bit ARM kernels, build tested on BMIPS_GENERIC:

Signature · 1 line
Florian
Tested-by on the v1 series
#397 lore raw
In reply to Greg Kroah-Hartman

Am 30.09.2026 um 17:24 schrieb Greg Kroah-Hartman:

Quoted 4 lines

This is the start of the stable review cycle for the 6.18.55 release. There are 395 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
#398 lore raw
In reply to Greg Kroah-Hartman

Hi!

Quoted 4 lines

This is the start of the stable review cycle for the 6.18.55 release. There are 395 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.

CIP testing did not find any problems here:

https://gitlab.com/cip-project/cip-testing/linux-stable-rc-ci/-/tree/linux-6.18.y

Best regards,
Pavel

Tested-by on the v1 series
#399 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 6.18.55 release. There are 395 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 Fri, 02 Oct 2026 15:23:04 +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/v6.x/stable-review/patch-6.18.55-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-6.18.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
#400 lore raw
In reply to Greg Kroah-Hartman
Quoted 8 lines · On Wed, 30 Sep 2026 17:24:22 +0200 Greg Kroah-Hartman…

This is the start of the stable review cycle for the 6.18.55 release. There are 395 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 Fri, 02 Oct 2026 15:23:04 +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
#402 lore raw
In reply to Greg Kroah-Hartman

Build tested in our x86,arm64,loongarch,riscv64 config successfully without error.

Best Regards
Wentao Guan

log:
Linux version 6.18.55-rc1-gad4aa0f0db99 (guanwentao@uos-PC) (aarch64-linux-gnu-gcc-12 (Deepin 12.3.0-17deepin8) 12.3.0, GNU ld (GNU Binutils for Deepin) 2.41) #3 SMP PREEMPT_DYNAMIC Fri Oct 2 03:09:51 CST 2026 Linux version 6.18.55-rc1-gad4aa0f0db99 (guanwentao@uos-PC) (loongarch64-linux-gnu-gcc-12 (Deepin 12.3.0-17deepin8) 12.3.0, GNU ld (GNU Binutils for Deepin) 2.41) #4 SMP PREEMPT_DYNAMIC Fri Oct 2 03:30:35 CST 2026 Linux version 6.18.55-rc1+ (guanwentao@uos-PC) (riscv64-linux-gnu-gcc-12 (Deepin 12.3.0-17deepin8) 12.3.0, GNU ld (GNU Binutils for Deepin) 2.41) #5 SMP PREEMPT Fri Oct 2 03:49:50 CST 2026 Linux version 6.18.55-rc1-gad4aa0f0db99 (guanwentao@uos-PC) (gcc (Deepin 12.3.0-17deepin18) 12.3.0, GNU ld (GNU Binutils for Deepin) 2.41) #2 SMP PREEMPT_DYNAMIC Fri Oct 2 02:47:53 CST 2026

Tested-by on the v1 series
#403 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 6.18.55 release. There are 395 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 Fri, 02 Oct 2026 15:23:04 +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/v6.x/stable-review/patch-6.18.55-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-6.18.y
and the diffstat can be found below.

thanks,

greg k-h

Tested on my amd64 DIY NAS and an arm64 virtual machine. Working well, no regressions observed.

Signature · 1 line
-Barry K. Nathan  <[email protected]>