Skip to contentSkip to conversation

7.2.9-rc1 review

PATCH In review Greg Kroah-Hartman 12 participants 469 messages Sep 30 – Oct 2 Page 3 of 4

Subsystems (6)
lore
Share & tools
lore
author maintainer
#263 lore raw

PATCH 263/457 fsl/fman: Fix clk reference leak in read_dts_node()

In reply to Greg Kroah-Hartman

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

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

From: Wentao Liang <[email protected]>

commit a644f09b2090ad22a13fbcf9d141084f573108ef upstream.

of_clk_get() returns a clock with its reference count incremented, but read_dts_node() only uses it to read the rate and never calls clk_put(). The clock is not stored anywhere, so the reference cannot be released later either.

Release the clock once its rate has been read, which also covers the error path taken when the rate is zero.

---
 drivers/net/ethernet/freescale/fman/fman.c |    1 +
 1 file changed, 1 insertion(+)
drivers/net/ethernet/freescale/fman/fman.c +1 -0
@@ -2734,6 +2734,7 @@ static struct fman *read_dts_node(struct
 	}
 
 	clk_rate = clk_get_rate(clk);
+	clk_put(clk);
 	if (!clk_rate) {
 		err = -EINVAL;
 		dev_err(&of_dev->dev, "%s: Failed to determine FM%d clock rate\n",
author maintainer
#264 lore raw

PATCH 264/457 ipe: fix use-after-free when auditing a newly loaded policy

In reply to Greg Kroah-Hartman

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

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

From: Fan Wu <[email protected]>

commit 9814077275eca36ebf8d510d2f076d235ff9f51a upstream.

new_policy() audits the policy after ipe_new_policyfs_node() publishes it and drops the new directory's inode lock. A concurrent delete can free the policy while ipe_audit_policy_load() is still using it.

Audit the successful load under that lock.

Fixes f44554b5067b ("audit,ipe: add IPE auditing support")
Assisted-by LLM
[FW: remove model name according to latest guideline]
---
 security/ipe/fs.c        |    8 +++-----
 security/ipe/policy_fs.c |    3 +++
 2 files changed, 6 insertions(+), 5 deletions(-)
security/ipe/fs.c +3 -5
@@ -159,18 +159,16 @@ static ssize_t new_policy(struct file *f
 	}
 
 	rc = ipe_new_policyfs_node(p);
-	if (rc)
-		goto out;
 
 out:
 	kfree(copy);
 	if (rc < 0) {
 		ipe_free_policy(p);
 		ipe_audit_policy_load(ERR_PTR(rc));
-	} else {
-		ipe_audit_policy_load(p);
+		return rc;
 	}
-	return (rc < 0) ? rc : len;
+
+	return len;
 }
 
 static const struct file_operations np_fops = {
security/ipe/policy_fs.c +3 -0
@@ -481,6 +481,9 @@ int ipe_new_policyfs_node(struct ipe_pol
 	inode_lock(root);
 	p->policyfs = policyfs;
 	root->i_private = p;
+	/* Only audit signed policies from userspace */
+	if (p->pkcs7)
+		ipe_audit_policy_load(p);
 	inode_unlock(root);
 
 	return 0;
author maintainer
#265 lore raw

PATCH 265/457 ipe: protect the dm-verity root hash with RCU

In reply to Greg Kroah-Hartman

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

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

From: Fan Wu <[email protected]>

commit 2776e9c28513a1c855a94792b292cbcc533418c8 upstream.

ipe_bdev_setintegrity() frees the old root hash when dm-verity publishes a new one on ->preresume, while policy evaluation can still be dereferencing it.

Protect the root hash with RCU. The evaluation path already runs under rcu_read_lock().

Fixes e155858dd995 ("ipe: add support for dm-verity as a trust provider")
Assisted-by LLM
[FW: remove model name according to latest guideline]
---
 security/ipe/eval.c  |   12 ++++++++----
 security/ipe/eval.h  |    2 +-
 security/ipe/hooks.c |   22 +++++++++++++++++-----
 3 files changed, 26 insertions(+), 10 deletions(-)
security/ipe/eval.c +8 -4
@@ -134,10 +134,14 @@ static bool evaluate_boot_verified(const
 static bool evaluate_dmv_roothash(const struct ipe_eval_ctx *const ctx,
 				  struct ipe_prop *p)
 {
-	return !!ctx->ipe_bdev &&
-	       !!ctx->ipe_bdev->root_hash &&
-	       ipe_digest_eval(p->value,
-			       ctx->ipe_bdev->root_hash);
+	const struct digest_info *root_hash;
+
+	if (!ctx->ipe_bdev)
+		return false;
+
+	root_hash = rcu_dereference(ctx->ipe_bdev->root_hash);
+
+	return root_hash && ipe_digest_eval(p->value, root_hash);
 }
 #else
 static bool evaluate_dmv_roothash(const struct ipe_eval_ctx *const ctx,
security/ipe/eval.h +1 -1
@@ -27,7 +27,7 @@ struct ipe_bdev {
 #ifdef CONFIG_IPE_PROP_DM_VERITY_SIGNATURE
 	bool dm_verity_signed;
 #endif /* CONFIG_IPE_PROP_DM_VERITY_SIGNATURE */
-	struct digest_info *root_hash;
+	struct digest_info __rcu *root_hash;
 };
 #endif /* CONFIG_IPE_PROP_DM_VERITY */
 
security/ipe/hooks.c +17 -5
@@ -9,6 +9,7 @@
 #include <linux/binfmts.h>
 #include <linux/mman.h>
 #include <linux/blk_types.h>
+#include <linux/rcupdate.h>
 
 #include "ipe.h"
 #include "hooks.h"
@@ -232,7 +233,20 @@ void ipe_bdev_free_security(struct block
 {
 	struct ipe_bdev *blob = ipe_bdev(bdev);
 
-	ipe_digest_free(blob->root_hash);
+	ipe_digest_free(rcu_access_pointer(blob->root_hash));
+}
+
+static void ipe_set_dmverity_roothash(struct ipe_bdev *blob,
+				      struct digest_info *info)
+{
+	struct digest_info *old;
+
+	/* Protected by device-mapper's md->suspend_lock */
+	old = rcu_replace_pointer(blob->root_hash, info, true);
+	if (old) {
+		synchronize_rcu();
+		ipe_digest_free(old);
+	}
 }
 
 #ifdef CONFIG_IPE_PROP_DM_VERITY_SIGNATURE
@@ -280,8 +294,7 @@ int ipe_bdev_setintegrity(struct block_d
 		return -EINVAL;
 
 	if (!value) {
-		ipe_digest_free(blob->root_hash);
-		blob->root_hash = NULL;
+		ipe_set_dmverity_roothash(blob, NULL);
 
 		return 0;
 	}
@@ -301,8 +314,7 @@ int ipe_bdev_setintegrity(struct block_d
 
 	info->digest_len = digest->digest_len;
 
-	ipe_digest_free(blob->root_hash);
-	blob->root_hash = info;
+	ipe_set_dmverity_roothash(blob, info);
 
 	return 0;
 err:
author maintainer
#266 lore raw

PATCH 266/457 ipv6: do not let ipv6_find_hdr() return an offset past the packet end

In reply to Greg Kroah-Hartman

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

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

From: Norbert Szetei <[email protected]>

commit ee319bd3a0e976af5087cbe59ebc50a66f31d202 upstream.

ipv6_find_hdr() walks the extension header chain, skipping each header by the length that header itself declares. ipv6_optlen() returns up to 2048, and the skip is never checked against skb->len, so the offset stored in *offset can point past the end of the packet.

openvswitch installs that offset as the transport header, and update_ipv6_checksum() then reads and writes the transport checksum field out of bounds:

  BUG: KASAN: slab-use-after-free in inet_proto_csum_replace16+0x445/0x470
  Read of size 2 at addr ffff88810b754b06 by task ovs_ipv6_oob/629
  CPU: 4 UID: 1000 PID: 629 Comm: ovs_ipv6_oob Tainted: G N 7.3.0-rc3+ #348
  Call Trace:
   inet_proto_csum_replace16+0x445/0x470
   set_ipv6_addr+0x3dd/0x460
   do_execute_actions+0x6a3d/0x7c40
   ovs_execute_actions+0xfd/0x480
   ovs_packet_cmd_execute+0xc38/0xf20
   genl_rcv_msg+0x59e/0x870
   netlink_rcv_skb+0x18b/0x450
   genl_rcv+0x2d/0x40
   netlink_unicast+0x6bc/0xa20

  The buggy address belongs to the object at ffff88810b754980
   which belongs to the cache skbuff_small_head of size 704
  The buggy address is located 390 bytes inside of
   freed 704-byte region [ffff88810b754980, ffff88810b754c40)

Other callers use that offset too, so bound it here rather than in one caller.

Reject a header whose declared length does not fit in the packet. ipv6_find_hdr() already fails with -EBADMSG on a malformed chain, so this adds no new failure mode.

---
 net/ipv6/exthdrs_core.c |    3 +++
 1 file changed, 3 insertions(+)
net/ipv6/exthdrs_core.c +3 -0
@@ -278,6 +278,9 @@ int ipv6_find_hdr(const struct sk_buff *
 			hdrlen = ipv6_optlen(hp);
 
 		if (!found) {
+			if (skb->len - start < hdrlen)
+				return -EBADMSG;
+
 			nexthdr = hp->nexthdr;
 			start += hdrlen;
 		}
author maintainer
#267 lore raw

PATCH 267/457 ipv6: sr: enforce exact attribute length for SEG6_ATTR_DST

In reply to Greg Kroah-Hartman

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

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

From: Hui Peng <[email protected]>

commit 2d959c75c27f90e9ec489d18ce5ee6b852ad4741 upstream.

In seg6_genl_policy, SEG6_ATTR_DST is defined with .type = NLA_BINARY and .len = sizeof(struct in6_addr). For NLA_BINARY, .len only enforces the maximum payload length and permits shorter payloads (e.g., 0 bytes). When seg6_genl_set_tunsrc() copies sizeof(struct in6_addr) bytes via kmemdup(val, sizeof(*val), GFP_KERNEL), a short SEG6_ATTR_DST attribute triggers a 16-byte out-of-bounds read past skb->tail into uninitialized skb->head memory, which is stored in sdata->tun_src and leaked back to userspace via SEG6_CMD_GET_TUNSRC.

Switch SEG6_ATTR_DST in seg6_genl_policy to NLA_POLICY_EXACT_LEN(sizeof(struct in6_addr)) so that generic netlink validation rejects any attribute whose length is not exactly sizeof(struct in6_addr) with -ERANGE.

Tested in QEMU against Linux 7.3.0-rc3 by sending a SEG6_CMD_SET_TUNSRC Generic Netlink message with a 0-byte SEG6_ATTR_DST attribute followed by SEG6_CMD_GET_TUNSRC. On the unfixed kernel, SEG6_CMD_SET_TUNSRC succeeds (err = 0) and SEG6_CMD_GET_TUNSRC leaks 16 bytes of uninitialized kernel heap memory (tun_src = 836a61ecc4d25a1042a8d60411cfb378); with this patch applied, SEG6_CMD_SET_TUNSRC is rejected by netlink policy validation with -ERANGE (-34) and tun_src remains zeroed.

---
 net/ipv6/seg6.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
net/ipv6/seg6.c +2 -2
@@ -138,8 +138,8 @@ out:
 static struct genl_family seg6_genl_family;
 
 static const struct nla_policy seg6_genl_policy[SEG6_ATTR_MAX + 1] = {
-	[SEG6_ATTR_DST]				= { .type = NLA_BINARY,
-		.len = sizeof(struct in6_addr) },
+	[SEG6_ATTR_DST]				=
+		NLA_POLICY_EXACT_LEN(sizeof(struct in6_addr)),
 	[SEG6_ATTR_DSTLEN]			= { .type = NLA_S32, },
 	[SEG6_ATTR_HMACKEYID]		= { .type = NLA_U32, },
 	[SEG6_ATTR_SECRET]			= { .type = NLA_BINARY, },
author maintainer
#268 lore raw

PATCH 268/457 kprobes: Fix permanent hang when flushing the kprobe optimizer

In reply to Greg Kroah-Hartman

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

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

From: Andrea Parri <[email protected]>

commit 5bfa9f1a9dcb6ecb607adbc1c0226605c972935b upstream.

Writing 0 to /proc/sys/debug/kprobes-optimization while a kprobe is jump-optimized never returns. The writer sleeps in D state forever with kprobe_sysctl_mutex held, so any later read or write of that sysctl hangs as well. For example, with vfs_read+9 as an optimizable address in this build:

  # cd /sys/kernel/tracing
  # echo 'p:myprobe vfs_read+9' >> kprobe_events
  # echo 1 > events/kprobes/myprobe/enable
  # # wait until /sys/kernel/debug/kprobes/list shows [OPTIMIZED]
  # echo 0 > /proc/sys/debug/kprobes-optimization
  INFO: task sh:246 blocked for more than 10 seconds.
  Call Trace:
   <TASK>
   __schedule+0x1176/0x4f70
   schedule+0xdc/0x2c0
   schedule_timeout+0x17b/0x260
   wait_for_completion+0x173/0x3c0
   wait_for_kprobe_optimizer_locked+0xbc/0x130
   proc_kprobes_optimization_handler+0x156/0x1b0
   proc_sys_call_handler+0x324/0x490
   vfs_write+0x52d/0xfe0
   ksys_write+0xff/0x200
   do_syscall_64+0x106/0x630
   entry_SYSCALL_64_after_hwframe+0x77/0x7f
   </TASK>
  ...
  INFO: task cat:265 is blocked on a mutex likely owned by task sh:246.

wait_for_kprobe_optimizer_locked() reinitializes optimizer_completion, asks the optimizer thread to flush and sleeps in wait_for_completion(). The thread drains the (un)optimizing lists, but calls complete() only if completion_done() is true, i.e. if the completion is already done, which never happens while someone waits. disarm_all_kprobes() and kprobe_trace_self_tests_init() wait the same way.

Calling complete() unconditionally would not be enough: the waiter drops kprobe_mutex while it sleeps, and nothing else serializes the sysctl handler against the debugfs "enabled" file. A second flusher that still finds the lists non-empty, e.g. because a disabled probe is queued for unoptimizing, reinitializes the completion under the first:

sysctl write debugfs "enabled" write
unoptimize_all_kprobes()

    wait_for_kprobe_optimizer_locked()
      init_completion(c)
      mutex_unlock(&kprobe_mutex)
      wait_for_completion(c)
                                    disarm_all_kprobes()
                                      wait_for_kprobe_optimizer_locked()
                                        init_completion(c)
                                          // c->wait is reset, the first
                                          // waiter is off the queue
                                        mutex_unlock(&kprobe_mutex)
                                        wait_for_completion(c)
  kprobe_optimizer()
    complete(c)
      // wakes the debugfs writer only

where c is &optimizer_completion. Lining up the two writes during an optimizer pass loses the sysctl writer this way.

Replace the completion with a counter of optimizer passes, bumped at the end of each pass and signalled with wake_up_var_locked(), both under kprobe_mutex. A flusher samples the count and waits with wait_var_event_mutex(), which drops kprobe_mutex only while sleeping, so a new count means a whole pass ran in the meantime. Nothing is reinitialized, so several flushers can sleep in the wait at once.

Fixes 73c12f209462 ("kprobes: Use dedicated kthread for kprobe optimizer")
Assisted-by LLM
---
 kernel/kprobes.c | 22 ++++++++++++++--------
 1 file changed, 14 insertions(+), 8 deletions(-)
kernel/kprobes.c +14 -8
index 6337da5cab9e..4edd8ca5c657 100644
@@ -42,6 +42,7 @@
 #include <linux/execmem.h>
 #include <linux/cleanup.h>
 #include <linux/wait.h>
+#include <linux/wait_bit.h>
 
 #include <asm/sections.h>
 #include <asm/cacheflush.h>
@@ -526,7 +527,8 @@ enum {
 	OPTIMIZER_ST_FLUSHING = 2,
 };
 
-static DECLARE_COMPLETION(optimizer_completion);
+/* Bumped at the end of each kprobe_optimizer() pass, under 'kprobe_mutex' */
+static unsigned long optimizer_passes;
 
 #define OPTIMIZE_DELAY 5
 
@@ -654,9 +656,9 @@ static void kprobe_optimizer(void)
 		do_free_cleaned_kprobes();
 	}
 
-	/* Step 5: Kick optimizer again if needed. But if there is a flush requested, */
-	if (completion_done(&optimizer_completion))
-		complete(&optimizer_completion);
+	/* Step 5: Wake up flushers, and kick optimizer again if needed. */
+	optimizer_passes++;
+	wake_up_var_locked(&optimizer_passes, &kprobe_mutex);
 
 	if (!list_empty(&optimizing_list) || !list_empty(&unoptimizing_list))
 		kick_kprobe_optimizer();	/*normal kick*/
@@ -708,7 +710,8 @@ static void wait_for_kprobe_optimizer_locked(void)
 	lockdep_assert_held(&kprobe_mutex);
 
 	while (!list_empty(&optimizing_list) || !list_empty(&unoptimizing_list)) {
-		init_completion(&optimizer_completion);
+		unsigned long passes = optimizer_passes;
+
 		/*
 		 * Set state to OPTIMIZER_ST_FLUSHING and wake up the thread if it's
 		 * idle. If it's already kicked, it will see the state change.
@@ -717,9 +720,12 @@ static void wait_for_kprobe_optimizer_locked(void)
 			OPTIMIZER_ST_FLUSHING) != OPTIMIZER_ST_FLUSHING)
 			wake_up(&kprobe_optimizer_wait);
 
-		mutex_unlock(&kprobe_mutex);
-		wait_for_completion(&optimizer_completion);
-		mutex_lock(&kprobe_mutex);
+		/*
+		 * kprobe_optimizer() holds 'kprobe_mutex' for a whole pass, which
+		 * this drops while sleeping, so a new count means a full pass ran.
+		 */
+		wait_var_event_mutex(&optimizer_passes,
+				     optimizer_passes != passes, &kprobe_mutex);
 	}
 }
 
Signature · 1 line
2.55.0
author maintainer
#269 lore raw

PATCH 269/457 gpio: arizona: Fix runtime PM leak in arizona_gpio_direction_out()

In reply to Greg Kroah-Hartman

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

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

From: Wentao Liang <[email protected]>

commit e9d810279f84b30738f7790c0ed15f8dd5b9024a upstream.

Switching a persistent GPIO line from input to output acquires a runtime PM reference on the parent device, but if the subsequent regmap_update_bits() fails the reference is never dropped and no later direction_in() can balance it since the direction was never changed. Drop the reference on the update failure path.

Fixes 27a49ed17e22 ("gpio: arizona: Add support for GPIOs that need to be maintained")
---
 drivers/gpio/gpio-arizona.c |    8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)
drivers/gpio/gpio-arizona.c +6 -2
@@ -115,8 +115,12 @@ static int arizona_gpio_direction_out(st
 	if (value)
 		value = ARIZONA_GPN_LVL;
 
-	return regmap_update_bits(arizona->regmap, ARIZONA_GPIO1_CTRL + offset,
-				  ARIZONA_GPN_DIR | ARIZONA_GPN_LVL, value);
+	ret = regmap_update_bits(arizona->regmap, ARIZONA_GPIO1_CTRL + offset,
+				 ARIZONA_GPN_DIR | ARIZONA_GPN_LVL, value);
+	if (ret < 0 && (val & ARIZONA_GPN_DIR) && persistent)
+		pm_runtime_put_autosuspend(chip->parent);
+
+	return ret;
 }
 
 static int arizona_gpio_set(struct gpio_chip *chip, unsigned int offset,
author maintainer
#270 lore raw

PATCH 270/457 gpio: cdev: fix kernel stack leak to user-space in error path

In reply to Greg Kroah-Hartman

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

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

From: Bartosz Golaszewski <[email protected]>

commit 1feb5d39b05afd902ed9fc902ec5b15be03a4bdb upstream.

If we fail to acquire the GPIO chip guard in gpio_desc_to_lineinfo(), we return immediately before zeroing the info struct we'll end up passing to the user-space later in lineinfo_get_v1(). This may leak the kernel stack contents. Make gpio_desc_to_lineinfo() return int so that the -ENODEV returned on failure to acquire the guard can be propagated to the callers.

While not strictly necessary: move the memset() before trying to acquire the SRCU read lock too for good measure.

---
 drivers/gpio/gpiolib-cdev.c |   32 +++++++++++++++++++++++++-------
 1 file changed, 25 insertions(+), 7 deletions(-)
drivers/gpio/gpiolib-cdev.c +25 -7
@@ -2172,18 +2172,19 @@ static void gpio_v2_line_info_changed_to
 
 #endif /* CONFIG_GPIO_CDEV_V1 */
 
-static void gpio_desc_to_lineinfo(struct gpio_desc *desc,
-				  struct gpio_v2_line_info *info, bool atomic)
+static int gpio_desc_to_lineinfo(struct gpio_desc *desc,
+				 struct gpio_v2_line_info *info, bool atomic)
 {
 	u32 debounce_period_us;
 	unsigned long dflags;
 	const char *label;
 
+	memset(info, 0, sizeof(*info));
+
 	CLASS(gpio_chip_guard, guard)(desc);
 	if (!guard.gc)
-		return;
+		return -ENODEV;
 
-	memset(info, 0, sizeof(*info));
 	info->offset = gpiod_hwgpio(desc);
 
 	if (desc->name)
@@ -2258,6 +2259,8 @@ static void gpio_desc_to_lineinfo(struct
 							debounce_period_us;
 		info->num_attrs++;
 	}
+
+	return 0;
 }
 
 struct gpio_chardev_data {
@@ -2309,6 +2312,7 @@ static int lineinfo_get_v1(struct gpio_c
 	struct gpio_desc *desc;
 	struct gpioline_info lineinfo;
 	struct gpio_v2_line_info lineinfo_v2;
+	int ret;
 
 	if (copy_from_user(&lineinfo, ip, sizeof(lineinfo)))
 		return -EFAULT;
@@ -2326,7 +2330,10 @@ static int lineinfo_get_v1(struct gpio_c
 			return -EBUSY;
 	}
 
-	gpio_desc_to_lineinfo(desc, &lineinfo_v2, false);
+	ret = gpio_desc_to_lineinfo(desc, &lineinfo_v2, false);
+	if (ret)
+		return ret;
+
 	gpio_v2_line_info_to_v1(&lineinfo_v2, &lineinfo);
 
 	if (copy_to_user(ip, &lineinfo, sizeof(lineinfo))) {
@@ -2344,6 +2351,7 @@ static int lineinfo_get(struct gpio_char
 {
 	struct gpio_desc *desc;
 	struct gpio_v2_line_info lineinfo;
+	int ret;
 
 	if (copy_from_user(&lineinfo, ip, sizeof(lineinfo)))
 		return -EFAULT;
@@ -2363,7 +2371,10 @@ static int lineinfo_get(struct gpio_char
 		if (test_and_set_bit(lineinfo.offset, cdev->watched_lines))
 			return -EBUSY;
 	}
-	gpio_desc_to_lineinfo(desc, &lineinfo, false);
+
+	ret = gpio_desc_to_lineinfo(desc, &lineinfo, false);
+	if (ret)
+		return ret;
 
 	if (copy_to_user(ip, &lineinfo, sizeof(lineinfo))) {
 		if (watch)
@@ -2489,6 +2500,7 @@ static int lineinfo_changed_notify(struc
 	struct lineinfo_changed_ctx *ctx;
 	struct gpio_desc *desc = data;
 	struct file *fp;
+	int ret;
 
 	if (!test_bit(gpiod_hwgpio(desc), cdev->watched_lines))
 		return NOTIFY_DONE;
@@ -2519,7 +2531,13 @@ static int lineinfo_changed_notify(struc
 
 	ctx->chg.event_type = action;
 	ctx->chg.timestamp_ns = ktime_get_ns();
-	gpio_desc_to_lineinfo(desc, &ctx->chg.info, true);
+
+	ret = gpio_desc_to_lineinfo(desc, &ctx->chg.info, true);
+	if (ret) {
+		fput(fp);
+		return NOTIFY_DONE;
+	}
+
 	/* Keep the GPIO device alive until we emit the event. */
 	ctx->gdev = gpio_device_get(desc->gdev);
 	ctx->cdev = cdev;
author maintainer
#271 lore raw

PATCH 271/457 gpio: zynq: fix runtime PM leak on request error path

In reply to Greg Kroah-Hartman

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

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

From: Ridham Khurana <[email protected]>

commit e9438ab5328a177c9c0e5df87eb92a7162841e98 upstream.

pm_runtime_get_sync() leaves the usage counter incremented even when it fails, and zynq_gpio_request() returns the error without dropping it.

gpiolib does not call ->free() when ->request() fails, so zynq_gpio_free(), which holds the only matching pm_runtime_put(), never runs. The reference is leaked and the controller can no longer runtime-suspend, so its clock stays enabled.

Switch to pm_runtime_resume_and_get(), which only increments the usage counter on success.

---
 drivers/gpio/gpio-zynq.c |   10 +---------
 1 file changed, 1 insertion(+), 9 deletions(-)
drivers/gpio/gpio-zynq.c +1 -9
@@ -798,15 +798,7 @@ static int zynq_gpio_runtime_resume(stru
 
 static int zynq_gpio_request(struct gpio_chip *chip, unsigned int offset)
 {
-	int ret;
-
-	ret = pm_runtime_get_sync(chip->parent);
-
-	/*
-	 * If the device is already active pm_runtime_get() will return 1 on
-	 * success, but gpio_request still needs to return 0.
-	 */
-	return ret < 0 ? ret : 0;
+	return pm_runtime_resume_and_get(chip->parent);
 }
 
 static void zynq_gpio_free(struct gpio_chip *chip, unsigned int offset)
author maintainer
#272 lore raw

PATCH 272/457 llc: reserve device headroom for allocated frames

In reply to Greg Kroah-Hartman

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

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

From: Zixuan Chai <[email protected]>

commit 72b5b9a28b996e09b8b5b944370c79851bb68f52 upstream.

llc_alloc_frame() reserves link-layer headroom using the device type. This is insufficient for stacked Ethernet devices such as VLAN devices, where vlan_dev_hard_header() pushes a VLAN header before the lower device's Ethernet header. An LLC response on such a device can therefore underflow skb headroom in eth_header().

Use LL_RESERVED_SPACE() to account for the device's actual required headroom while preserving the existing LLC device-type check.

---
 net/llc/llc_sap.c |    8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)
net/llc/llc_sap.c +4 -4
@@ -19,12 +19,12 @@
 #include <linux/llc.h>
 #include <linux/slab.h>
 
-static int llc_mac_header_len(unsigned short devtype)
+static int llc_mac_header_len(struct net_device *dev)
 {
-	switch (devtype) {
+	switch (dev->type) {
 	case ARPHRD_ETHER:
 	case ARPHRD_LOOPBACK:
-		return sizeof(struct ethhdr);
+		return LL_RESERVED_SPACE(dev);
 	}
 	return 0;
 }
@@ -45,7 +45,7 @@ struct sk_buff *llc_alloc_frame(struct s
 	int hlen = type == LLC_PDU_TYPE_U ? 3 : 4;
 	struct sk_buff *skb;
 
-	hlen += llc_mac_header_len(dev->type);
+	hlen += llc_mac_header_len(dev);
 	skb = alloc_skb(hlen + data_size, GFP_ATOMIC);
 
 	if (skb) {
author maintainer
#273 lore raw

PATCH 273/457 mctp: route: iterate socket tag list in mctp_lookup_prealloc_tag()

In reply to Greg Kroah-Hartman

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

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

From: Hui Peng <[email protected]>

commit 26cc0e69cce062cd3aa6fae33074684669c35a71 upstream.

When a socket transmits a packet with MCTP_TAG_PREALLOC set, mctp_lookup_prealloc_tag() iterates over the per-netns &mns->keys list and matches netid, req_tag, peer_addr, and manual_alloc, without checking whether tmp->sk == &msk->sk. This allows any MCTP socket in the same network namespace to use and consume another socket's preallocated tag.

Iterate the socket's own tag list (&msk->keys via sklist) instead of the namespace-wide &mns->keys list in mctp_lookup_prealloc_tag(), ensuring that only tags allocated by msk are matched.

Tested in QEMU against Linux 7.3.0-rc3 by allocating a manual tag (0x18) on socket A via SIOCMCTPALLOCTAG for peer EID 9 and sending a 4-byte message with MCTP_TAG_PREALLOC from socket B in the same network namespace. On the unfixed kernel, sendto(sock_b) using socket A's preallocated tag succeeds (ret = 4); with this patch applied, sendto(sock_b) fails with -ENOENT (errno = 2) while sendto(sock_a) succeeds (ret = 4).

Fixes 63ed1aab3d40 ("mctp: Add SIOCMCTP{ALLOC,DROP}TAG ioctls for tag control")
---
 net/mctp/route.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
net/mctp/route.c +1 -1
@@ -825,7 +825,7 @@ static struct mctp_sk_key *mctp_lookup_p
 
 	spin_lock_irqsave(&mns->keys_lock, flags);
 
-	hlist_for_each_entry(tmp, &mns->keys, hlist) {
+	hlist_for_each_entry(tmp, &msk->keys, sklist) {
 		if (tmp->net != netid)
 			continue;
 
author maintainer
#274 lore raw

PATCH 274/457 rds: ib: Clear the sg list when mapping an MR fails

In reply to Greg Kroah-Hartman

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

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

From: Dongliang Qin <[email protected]>

commit 58eb1b3325edac42dc6df72c80962bd53a3c8ca7 upstream.

rds_ib_map_frmr() stores the caller's scatterlist in the MR before DMA mapping and registration can fail. On failure, __rds_rdma_map() unpins the pages and frees the scatterlist, but rds_ib_free_frmr() can still return the MR to the pool with the stale pointer set.

This leaves the pool with a dangling scatterlist and can lead to local privilege escalation. KASAN detects the resulting use-after-free when the MR is later torn down:

BUG: KASAN: slab-use-after-free in __rds_ib_teardown_mr
Read of size 8

Call Trace:
 __rds_ib_teardown_mr
 rds_ib_unreg_frmr
 rds_ib_flush_mr_pool
 rds_ib_flush_mrs
 rds_free_mr
 rds_setsockopt

Store the scatterlist in the MR only after DMA mapping succeeds. If DMA mapping fails, return directly while the MR fields remain clear; the caller keeps ownership of the scatterlist and its pinned pages. If a later registration step fails, unmap the scatterlist and clear the MR fields before returning.

Fixes 1659185fb4d0 ("RDS: IB: Support Fastreg MR (FRMR) memory registration mode")
---
 net/rds/ib_frmr.c |   11 +++++------
 1 file changed, 5 insertions(+), 6 deletions(-)
net/rds/ib_frmr.c +5 -6
@@ -204,19 +204,16 @@ static int rds_ib_map_frmr(struct rds_ib
 	 */
 	rds_ib_teardown_mr(ibmr);
 
-	ibmr->sg = sg;
-	ibmr->sg_len = sg_len;
-	ibmr->sg_dma_len = 0;
 	frmr->sg_byte_len = 0;
-	WARN_ON(ibmr->sg_dma_len);
-	ibmr->sg_dma_len = ib_dma_map_sg(dev, ibmr->sg, ibmr->sg_len,
+	ibmr->sg_dma_len = ib_dma_map_sg(dev, sg, sg_len,
 					 DMA_BIDIRECTIONAL);
 	if (unlikely(!ibmr->sg_dma_len)) {
 		pr_warn("RDS/IB: %s failed!\n", __func__);
 		return -EBUSY;
 	}
 
-	frmr->sg_byte_len = 0;
+	ibmr->sg = sg;
+	ibmr->sg_len = sg_len;
 	frmr->dma_npages = 0;
 	len = 0;
 
@@ -264,6 +261,8 @@ out_unmap:
 	ib_dma_unmap_sg(rds_ibdev->dev, ibmr->sg, ibmr->sg_len,
 			DMA_BIDIRECTIONAL);
 	ibmr->sg_dma_len = 0;
+	ibmr->sg = NULL;
+	ibmr->sg_len = 0;
 	return ret;
 }
 
author maintainer
#275 lore raw

PATCH 275/457 drm/client: fix restore of partially initialized client

In reply to Greg Kroah-Hartman

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

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

From: shechenglong <[email protected]>

commit 1fca688e9443003e33cf30453e7a7560367656c9 upstream.

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
@@ -42,6 +42,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
#276 lore raw

PATCH 276/457 drm/imagination: Propagate map failures correctly from pvr_mmu_map_sgl()

In reply to Greg Kroah-Hartman

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

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

From: Brajesh Gupta <[email protected]>

commit 7b824c293a6b56de8285a97984c507cba56bc4c4 upstream.

Map failure from pvr_mmu_map_sgl() interface was not returned correctly to pvr_mmu_map() interface. This resulted in pvr_mmu_map() interface to continue instead of returning an error to caller.
Fix it by returning a proper error code from pvr_mmu_map_sgl() interface.

Call stack for crash:

[ 1179.286237] Unable to handle kernel NULL pointer dereference at virtual address 0000000000000008
[ 1179.295067] Mem abort info:
[ 1179.297877]   ESR = 0x0000000096000004
[ 1179.301656]   EC = 0x25: DABT (current EL), IL = 32 bits
[ 1179.306987]   SET = 0, FnV = 0
[ 1179.310048]   EA = 0, S1PTW = 0
[ 1179.313198]   FSC = 0x04: level 0 translation fault
[ 1179.318096] Data abort info:
[ 1179.320993]   ISV = 0, ISS = 0x00000004, ISS2 = 0x00000000
[ 1179.326483]   CM = 0, WnR = 0, TnD = 0, TagAccess = 0
[ 1179.331546]   GCS = 0, Overlay = 0, DirtyBit = 0, Xs = 0
[ 1179.336895] user pgtable: 4k pages, 48-bit VAs, pgdp=000000009822a000
[ 1179.343402] [0000000000000008] pgd=0000000000000000, p4d=0000000000000000
[ 1179.350243] Internal error: Oops: 0000000096000004 [#2]  SMP
[ 1179.355908] Modules linked in: powervr gpu_sched drm_shmem_helper drm_gpuvm drm_exec xhci_plat_hcd xhci_hcd dwc3 usbcore usb_common snd_soc_simple_card snd_soc_simple_card_utils dwc3_am62 at24 sa2ul sha512 libsha512 sha256 authenc sch_fq_codel fuse dm_mod ipv6
[ 1179.378992] CPU: 1 UID: 1000 PID: 680 Comm: deqp-vk Tainted: G      D             6.17.0 #1 PREEMPT
[ 1179.388120] Tainted: [D]=DIE
[ 1179.390994] Hardware name: Texas Instruments AM625 SK (DT)
[ 1179.396467] pstate: 00000005 (nzcv daif -PAN -UAO -TCO -DIT -SSBS BTYPE=--)
[ 1179.403415] pc : pvr_mmu_op_context_unmap_curr_page+0x6c/0x134 [powervr]
[ 1179.410140] lr : pvr_mmu_op_context_unmap_curr_page+0x58/0x134 [powervr]
[ 1179.416848] sp : ffff8000839ab8c0
[ 1179.420153] x29: ffff8000839ab8c0 x28: 0000000000000001 x27: 000000008f386000
[ 1179.427283] x26: ffff000016d1df98 x25: 0000000000247000 x24: 00000000000001e6
[ 1179.434413] x23: 0000000000000002 x22: 000000000000ffff x21: 0000000000000247
[ 1179.441540] x20: 0000000000000245 x19: ffff000016d1df60 x18: 0000000000000002
[ 1179.448668] x17: 0000000000000000 x16: 0000000000000000 x15: 0000000000000001
[ 1179.455793] x14: 0000000000060810 x13: ffff80007fffffff x12: ffff000004190480
[ 1179.462921] x11: ffff8000853f7000 x10: ffff8000811ae000 x9 : ffff0000041900b8
[ 1179.470051] x8 : 0000000000000000 x7 : 00000000990c4001 x6 : 0000000000000007
[ 1179.477177] x5 : ffff000016d1df60 x4 : 0000000000000000 x3 : ffff00000a7d8000
[ 1179.484306] x2 : 00000000000001ff x1 : 0000000000000000 x0 : 0000000000000000
[ 1179.491433] Call trace:
[ 1179.493872]  pvr_mmu_op_context_unmap_curr_page+0x6c/0x134 [powervr] (P)
[ 1179.500582]  pvr_mmu_map+0x31c/0x388 [powervr]
[ 1179.505027]  pvr_vm_gpuva_map+0x40/0x88 [powervr]
[ 1179.509732]  __drm_gpuvm_sm_map+0x250/0x44c [drm_gpuvm]
[ 1179.514952]  drm_gpuvm_sm_map+0x48/0x5c [drm_gpuvm]
[ 1179.519822]  pvr_vm_bind_op_exec+0x64/0x70 [powervr]
[ 1179.524785]  pvr_vm_map+0x1f8/0x2a8 [powervr]
[ 1179.529142]  pvr_ioctl_vm_map+0x12c/0x188 [powervr]
[ 1179.534018]  drm_ioctl_kernel+0xb8/0x128
[ 1179.537941]  drm_ioctl+0x21c/0x4ec
[ 1179.541337]  __arm64_sys_ioctl+0xac/0x108
[ 1179.545344]  invoke_syscall+0x44/0x100
[ 1179.549091]  el0_svc_common.constprop.0+0x40/0xe0
[ 1179.553790]  do_el0_svc+0x1c/0x28
[ 1179.557106]  el0_svc+0x34/0xf0
[ 1179.560159]  el0t_64_sync_handler+0xd0/0xe4
[ 1179.564334]  el0t_64_sync+0x198/0x19c
[ 1179.567996] Code: 54000300 35000360 f9402261 79409a62 (f9400421)
[ 1179.574081] ---[ end trace 0000000000000000 ]---
---
 drivers/gpu/drm/imagination/pvr_mmu.c |    5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)
drivers/gpu/drm/imagination/pvr_mmu.c +4 -1
@@ -12,6 +12,7 @@
 #include "pvr_rogue_mmu_defs.h"
 
 #include <drm/drm_drv.h>
+#include <drm/drm_print.h>
 #include <linux/atomic.h>
 #include <linux/bitops.h>
 #include <linux/dma-mapping.h>
@@ -2553,7 +2554,9 @@ pvr_mmu_map_sgl(struct pvr_mmu_op_contex
 
 err_destroy_pages:
 	memcpy(&op_ctx->curr_page, &ptr_copy, sizeof(op_ctx->curr_page));
-	err = pvr_mmu_op_context_unmap_curr_page(op_ctx, page);
+	if (pvr_mmu_op_context_unmap_curr_page(op_ctx, page))
+		drm_err(from_pvr_device(op_ctx->mmu_ctx->pvr_dev),
+			"%s : Failure in unmapping pages\n", __func__);
 
 	return err;
 }
author maintainer
#277 lore raw

PATCH 277/457 drm/imagination: Fix page count for page table for map() interface

In reply to Greg Kroah-Hartman

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

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

From: Brajesh Gupta <[email protected]>

commit 0a8224058a5835297dcf4a46bbcd16f77a9fe424 upstream.

The GPU virtual start address wasn't included in the calculation for the amount of page tables required for mapping a BO object in map() interface. It resulted in map failure later due to not enough pages at L0/L1 level. Update pvr_mmu_op_context_create() interface to pass device address as well to allow correct calculation for page table memory.

If L0 tables cover 2MB (0x200000), the range defined by device address 0x80001ff000 (general heap at 2MB - 4KB) and size 0x2000 (two 4KB pages) requires two L0 pages to be mapped, but without the base address a range of 0x2000 computes to a single L0 page which is not enough.

---
 drivers/gpu/drm/imagination/pvr_mmu.c |   14 ++++++++------
 drivers/gpu/drm/imagination/pvr_mmu.h |    2 +-
 drivers/gpu/drm/imagination/pvr_vm.c  |    4 ++--
 3 files changed, 11 insertions(+), 9 deletions(-)
drivers/gpu/drm/imagination/pvr_mmu.c +8 -6
@@ -2336,6 +2336,7 @@ void pvr_mmu_op_context_destroy(struct p
  * pvr_mmu_op_context_create() - Create an MMU op context.
  * @ctx: MMU context associated with owning VM context.
  * @sgt: Scatter gather table containing pages pinned for use by this context.
+ * @device_addr: Virtual device address at the start of the requested mapping.
  * @sgt_offset: Start offset of the requested device-virtual memory mapping.
  * @size: Size in bytes of the requested device-virtual memory mapping. For an
  * unmapping, this should be zero so that no page tables are allocated.
@@ -2347,8 +2348,9 @@ void pvr_mmu_op_context_destroy(struct p
  */
 struct pvr_mmu_op_context *
 pvr_mmu_op_context_create(struct pvr_mmu_context *ctx, struct sg_table *sgt,
-			  u64 sgt_offset, u64 size)
+			  u64 device_addr, u64 sgt_offset, u64 size)
 {
+	u64 start_addr = device_addr + sgt_offset;
 	int err;
 
 	struct pvr_mmu_op_context *op_ctx = kzalloc_obj(*op_ctx);
@@ -2364,16 +2366,16 @@ pvr_mmu_op_context_create(struct pvr_mmu
 	if (size) {
 		/*
 		 * The number of page table objects we need to prealloc is
-		 * indicated by the mapping size, start offset and the sizes
+		 * indicated by the mapping size, start address and the sizes
 		 * of the areas mapped per PT or PD. The range calculation is
 		 * identical to that for the index into a table for a device
 		 * address, so we reuse those functions here.
 		 */
-		const u32 l1_start_idx = pvr_page_table_l2_idx(sgt_offset);
-		const u32 l1_end_idx = pvr_page_table_l2_idx(sgt_offset + size);
+		const u32 l1_start_idx = pvr_page_table_l2_idx(start_addr);
+		const u32 l1_end_idx = pvr_page_table_l2_idx(start_addr + size);
 		const u32 l1_count = l1_end_idx - l1_start_idx + 1;
-		const u32 l0_start_idx = pvr_page_table_l1_idx(sgt_offset);
-		const u32 l0_end_idx = pvr_page_table_l1_idx(sgt_offset + size);
+		const u32 l0_start_idx = pvr_page_table_l1_idx(start_addr);
+		const u32 l0_end_idx = pvr_page_table_l1_idx(start_addr + size);
 		const u32 l0_count = l0_end_idx - l0_start_idx + 1;
 
 		/*
drivers/gpu/drm/imagination/pvr_mmu.h +1 -1
@@ -99,7 +99,7 @@ dma_addr_t pvr_mmu_get_root_table_dma_ad
 void pvr_mmu_op_context_destroy(struct pvr_mmu_op_context *op_ctx);
 struct pvr_mmu_op_context *
 pvr_mmu_op_context_create(struct pvr_mmu_context *ctx,
-			  struct sg_table *sgt, u64 sgt_offset, u64 size);
+			  struct sg_table *sgt, u64 device_addr, u64 sgt_offset, u64 size);
 
 int pvr_mmu_map(struct pvr_mmu_op_context *op_ctx, u64 size, u64 flags,
 		u64 device_addr);
drivers/gpu/drm/imagination/pvr_vm.c +2 -2
@@ -276,7 +276,7 @@ pvr_vm_bind_op_map_init(struct pvr_vm_bi
 		goto err_bind_op_fini;
 
 	bind_op->mmu_op_ctx =
-		pvr_mmu_op_context_create(vm_ctx->mmu_ctx, sgt, offset, size);
+		pvr_mmu_op_context_create(vm_ctx->mmu_ctx, sgt, device_addr, offset, size);
 	err = PTR_ERR_OR_ZERO(bind_op->mmu_op_ctx);
 	if (err) {
 		bind_op->mmu_op_ctx = NULL;
@@ -318,7 +318,7 @@ pvr_vm_bind_op_unmap_init(struct pvr_vm_
 	}
 
 	bind_op->mmu_op_ctx =
-		pvr_mmu_op_context_create(vm_ctx->mmu_ctx, NULL, 0, 0);
+		pvr_mmu_op_context_create(vm_ctx->mmu_ctx, NULL, device_addr, 0, 0);
 	err = PTR_ERR_OR_ZERO(bind_op->mmu_op_ctx);
 	if (err) {
 		bind_op->mmu_op_ctx = NULL;
author maintainer
#278 lore raw

PATCH 278/457 drm/i915: fix incorrect RCU teardown order

In reply to Greg Kroah-Hartman

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

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

From: Christian König <[email protected]>

commit d2da6696e0c4e60414706e607029d0bb0330c67e upstream.

i915_gem_busy_ioctl uses dma_resv_for_each_fence_unlocked() to iterate over the fences in an GEM object without holding a reference but only the RCU read side lock.

What can happen here is that the GEM object is destroyed concurrently while i915_gem_busy_ioctl is still running. This won't free the GEM objects memory, but still drops all the dma_fence references.

Now when dma_resv_for_each_fence_unlocked() sees a destroyed dma_fence it assumes that a new fence list was installed and re-starts the loop.

But in the case of a destroyed GEM object a new fence list is never installed, only the old one freed and therefore the iteration never finishes resulting in an endless loop.

The solution is to drop the fence references only after the RCU grace period.

The fixes tag is not necessary the patch introducing the problem, but the one making it so worse that we need to address it.

This problem was pointed out by Sashiko-bot.

Fixes 912ff2ebd695 ("drm/i915: use the new iterator in i915_gem_busy_ioctl v2")
(cherry picked from commit 5113479556025093bf8133bb2dcaa33be2d50921)
---
 drivers/gpu/drm/i915/gem/i915_gem_object.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/gpu/drm/i915/gem/i915_gem_object.c +1 -1
@@ -89,6 +89,7 @@ struct drm_i915_gem_object *i915_gem_obj
 
 void i915_gem_object_free(struct drm_i915_gem_object *obj)
 {
+	dma_resv_fini(&obj->base._resv);
 	return kmem_cache_free(slab_objects, obj);
 }
 
@@ -144,7 +145,6 @@ void __i915_gem_object_fini(struct drm_i
 {
 	mutex_destroy(&obj->mm.get_page.lock);
 	mutex_destroy(&obj->mm.get_dma_page.lock);
-	dma_resv_fini(&obj->base._resv);
 }
 
 /**
author maintainer
#279 lore raw

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

In reply to Greg Kroah-Hartman

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

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

From: Ankit Nautiyal <[email protected]>

commit 5271d81f99dd01d983d439930eb056952485e15e upstream.

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
#280 lore raw

PATCH 280/457 drm/i915/dp_mst: Fix configuring FEC for a disconnected stream

In reply to Greg Kroah-Hartman

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

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

From: Imre Deak <[email protected]>

commit acbe9a3b60b9a6ace8ef11fe898f586251c84592 upstream.

During an atomic commit after all the MST stream CRTC state is computed the driver ensures that the FEC is configured the same way (enabled or disabled) for all the streams on a given MST topology's link. drm_dp_mst_port_downstream_of_parent() used to determine if a stream is downstream of an MST port will return false if the whole topology is disconnected, since in that case it can't verify that the port/ parent_port passed to it is in the given MST topology. This is a problem during the above FEC configuration check, since intel_dp_mst_check_dsc_change()->get_pipes_downstream_of_mst_ports() will not return all the stream CRTCs/pipes for the topology as expected. Since passing parent_port==NULL to get_pipes_downstream_of_mst_port() is meant to return all the streams for the given topology (i.e. mst_mgr) skip checking if an MST port is downstream of a parent port in this case.

This fixes a problem where the FEC configuration check explained above failed to ensure that all streams' FEC is configured the same way if the topology was disconnected, leading to a FEC state mismatch error.

---
 drivers/gpu/drm/i915/display/intel_dp_mst.c |    3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/gpu/drm/i915/display/intel_dp_mst.c +2 -1
@@ -813,7 +813,8 @@ static u8 get_pipes_downstream_of_mst_po
 		if (&connector->mst.dp->mst.mgr != mst_mgr)
 			continue;
 
-		if (connector->mst.port != parent_port &&
+		if (parent_port &&
+		    connector->mst.port != parent_port &&
 		    !drm_dp_mst_port_downstream_of_parent(mst_mgr,
 							  connector->mst.port,
 							  parent_port))
author maintainer
#281 lore raw

PATCH 281/457 drm/i915/dp_mst: Fix configuring TUs for a disconnected stream

In reply to Greg Kroah-Hartman

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

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

From: Imre Deak <[email protected]>

commit a443e0b8d647c1401b110d9f919d8c6cb8607260 upstream.

During an atomic commit after all the MST stream CRTC state is computed the driver ensures that the sum of TUs of all the streams on a given MST topology link is within limits (63 for 8b10 and 64 for 128b132b). For a disconnected stream the DRM MST core's BW verification doesn't ensure this, because the topology state it uses for this is destroyed as soon as the stream (i.e. MST connector/port) is disconnected. The driver should keep the link state valid even for such disconnected streams, as userspace may disable them one-by-one only in a deferred way. Ensure the link's sum of TUs stays within limits in this case by simply reusing the maximum link BPP limit from the stream's (i.e. CRTC's) old state.

The disconnection can happen either via the whole topology getting disconnected or via only the given stream's port getting disconnected. Check for both of these conditions separately, as a connector gets unregistered after a link disconnect event only in a deferred way.

---
 drivers/gpu/drm/i915/display/intel_dp_mst.c  |   21 +++++++++++++++++++++
 drivers/gpu/drm/i915/display/intel_dp_mst.h  |    2 ++
 drivers/gpu/drm/i915/display/intel_link_bw.c |    3 ++-
 3 files changed, 25 insertions(+), 1 deletion(-)
drivers/gpu/drm/i915/display/intel_dp_mst.c +21 -0
@@ -2122,6 +2122,27 @@ bool intel_dp_mst_crtc_needs_modeset(str
 	return false;
 }
 
+bool intel_dp_mst_stream_disconnected(struct intel_atomic_state *state,
+				      const struct intel_crtc *crtc)
+{
+	struct intel_connector *connector;
+
+	connector = get_connector_in_state_for_crtc(state, crtc);
+	if (!connector)
+		return false;
+
+	if (!connector->mst.dp)
+		return false;
+
+	if (!connector->mst.dp->mst.mgr.mst_state)
+		return true;
+
+	if (drm_connector_is_unregistered(&connector->base))
+		return true;
+
+	return false;
+}
+
 /**
  * intel_dp_mst_prepare_probe - Prepare an MST link for topology probing
  * @intel_dp: DP port object
drivers/gpu/drm/i915/display/intel_dp_mst.h +2 -0
@@ -28,6 +28,8 @@ int intel_dp_mst_atomic_check_link(struc
 				   struct intel_link_bw_limits *limits);
 bool intel_dp_mst_crtc_needs_modeset(struct intel_atomic_state *state,
 				     struct intel_crtc *crtc);
+bool intel_dp_mst_stream_disconnected(struct intel_atomic_state *state,
+				      const struct intel_crtc *crtc);
 void intel_dp_mst_prepare_probe(struct intel_dp *intel_dp);
 bool intel_dp_mst_verify_dpcd_state(struct intel_dp *intel_dp);
 
drivers/gpu/drm/i915/display/intel_link_bw.c +2 -1
@@ -64,7 +64,8 @@ void intel_link_bw_init_limits(struct in
 			intel_atomic_get_new_crtc_state(state, crtc);
 		int forced_bpp_x16 = get_forced_link_bpp_x16(state, crtc);
 
-		if (state->base.duplicated && crtc_state) {
+		if ((state->base.duplicated && crtc_state) ||
+		    intel_dp_mst_stream_disconnected(state, crtc)) {
 			limits->max_bpp_x16[pipe] = crtc_state->max_link_bpp_x16;
 			if (intel_dsc_enabled_on_link(crtc_state))
 				limits->link_dsc_pipes |= BIT(pipe);
author maintainer
#282 lore raw

PATCH 282/457 drm/amdkfd: fix use-after-free and multi-container gap in kfd_dev_mapping

In reply to Greg Kroah-Hartman

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

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

From: Asad Kamal <[email protected]>

commit c3a31087b1c8df679b653c1a09d9abe5ff7ec8ef upstream.

kfd_dev_mapping caches the address_space of the first /dev/kfd opener so that the GPU reset path can call unmap_mapping_range() to zap all userspace mappings of doorbell and MMIO ranges. This design has two bugs that both manifest under SRIOV with multiple containers:

1. Use-after-free / rwsem deadlock. The cached pointer refers to an inode owned by the first opener's container. When that container exits and its inode is released, kfd_dev_mapping becomes a dangling pointer. A subsequent GPU reset dereferences it inside unmap_mapping_range(), which takes i_mmap_rwsem on the freed inode, causing a hard hang observable as an uninterruptible rwsem wait.

2. Multi-container gap. Only the first opener's address_space is cached; VMAs created by later openers live in a different address_space and are never reached by unmap_mapping_range(). After a GPU reset those stale mappings keep doorbell and MMIO pages accessible to guest userspace with no GPU behind them, risking PCIe transaction timeouts and NMI panics.

Fix both bugs with the same approach used by DRM core (drm_drv.c):
create a private pseudo-filesystem at module init time and allocate one anonymous inode from it. In kfd_open() redirect every opener's file->f_mapping to that inode's address_space. The inode is module-owned, lives exactly as long as the amdgpu module, and collects VMAs from all openers in one address_space. A single unmap_mapping_range() call in the reset path then correctly reaches every container's mappings with no dangling pointer risk.

The hang manifests as an NMI backtrace on the GPU reset workqueue stuck spinning in rwsem_down_read_slowpath() with a corrupted i_mmap_rwsem:

  Workqueue: amdgpu-reset-dev xgpu_ai_mailbox_flr_work [amdgpu]
  Call Trace:
   <TASK>
   kvm_wait+0x1f/0x40
   __pv_queued_spin_lock_slowpath+0x31d/0x3a0
   _raw_spin_lock_irq+0x51/0x80
   rwsem_down_read_slowpath+0xb3/0x550
   down_read+0x48/0xd0
   unmap_mapping_range+0x71/0x140
   kfd_dev_unmap_mapping_range+0x5b/0x140 [amdgpu]
   amdgpu_amdkfd_clear_kfd_mapping+0xd8/0x190 [amdgpu]
   amdgpu_device_gpu_recover+0x232/0x450 [amdgpu]
   xgpu_ai_mailbox_flr_work+0xb5/0xc0 [amdgpu]
   process_one_work+0x18e/0x3e0
   worker_thread+0x2e3/0x420
   kthread+0x10a/0x230
Fixes 70cadefcc616 ("drm/amdgpu: unmap all user mappings of framebuffer and doorbell before mode1 reset")
(cherry picked from commit 1128b4a52de1572e87431de837fd9850cb99542c)
---
 drivers/gpu/drm/amd/amdkfd/kfd_chardev.c | 71 +++++++++++++++++++-----
 1 file changed, 57 insertions(+), 14 deletions(-)
drivers/gpu/drm/amd/amdkfd/kfd_chardev.c +57 -14
index 504a286368eb..344da6c0e96a 100644
@@ -35,6 +35,7 @@
 #include <linux/time.h>
 #include <linux/mm.h>
 #include <linux/mman.h>
+#include <linux/pseudo_fs.h>
 #include <linux/ptrace.h>
 #include <linux/dma-buf.h>
 #include <linux/processor.h>
@@ -70,18 +71,54 @@ static const struct class kfd_class = {
 };
 
 /*
- * Cache the address space of the chardev on first open so that the reset
- * path can drop all userspace mappings of doorbell and MMIO ranges via
- * unmap_mapping_range().
+ * Private pseudo-filesystem for KFD, Provides a stable, module-owned
+ * inode whose address_space is the unmap target for all /dev/kfd
+ * openers during GPU reset.
  */
-static struct address_space *kfd_dev_mapping;
+static struct vfsmount *kfd_fs_mnt;
+static int kfd_fs_cnt;
+
+static int kfd_fs_init_fs_context(struct fs_context *fc)
+{
+	return init_pseudo(fc, 0x4b464400 /* "KFD" */) ? 0 : -ENOMEM;
+}
+
+static struct file_system_type kfd_fs_type = {
+	.name		= "kfd",
+	.init_fs_context = kfd_fs_init_fs_context,
+	.kill_sb	= kill_anon_super,
+};
+
+static struct inode *kfd_fs_inode_new(void)
+{
+	struct inode *inode;
+	int r;
+
+	r = simple_pin_fs(&kfd_fs_type, &kfd_fs_mnt, &kfd_fs_cnt);
+	if (r < 0)
+		return ERR_PTR(r);
+
+	inode = alloc_anon_inode(kfd_fs_mnt->mnt_sb);
+	if (IS_ERR(inode))
+		simple_release_fs(&kfd_fs_mnt, &kfd_fs_cnt);
+
+	return inode;
+}
+
+static void kfd_fs_inode_free(struct inode *inode)
+{
+	if (inode) {
+		iput(inode);
+		simple_release_fs(&kfd_fs_mnt, &kfd_fs_cnt);
+	}
+}
+
+static struct inode *kfd_anon_inode;
 
 void kfd_dev_unmap_mapping_range(loff_t const holebegin, loff_t const holelen)
 {
-	struct address_space *mapping = READ_ONCE(kfd_dev_mapping);
-
-	if (mapping)
-		unmap_mapping_range(mapping, holebegin, holelen, 1);
+	if (kfd_anon_inode)
+		unmap_mapping_range(kfd_anon_inode->i_mapping, holebegin, holelen, 1);
 }
 
 static inline struct kfd_process_device *kfd_lock_pdd_by_id(struct kfd_process *p, __u32 gpu_id)
@@ -107,6 +144,13 @@ int kfd_chardev_init(void)
 {
 	int err = 0;
 
+	kfd_anon_inode = kfd_fs_inode_new();
+	if (IS_ERR(kfd_anon_inode)) {
+		err = PTR_ERR(kfd_anon_inode);
+		kfd_anon_inode = NULL;
+		return err;
+	}
+
 	kfd_char_dev_major = register_chrdev(0, kfd_dev_name, &kfd_fops);
 	err = kfd_char_dev_major;
 	if (err < 0)
@@ -130,6 +174,8 @@ int kfd_chardev_init(void)
 err_class_create:
 	unregister_chrdev(kfd_char_dev_major, kfd_dev_name);
 err_register_chrdev:
+	kfd_fs_inode_free(kfd_anon_inode);
+	kfd_anon_inode = NULL;
 	return err;
 }
 
@@ -138,6 +184,8 @@ void kfd_chardev_exit(void)
 	device_destroy(&kfd_class, MKDEV(kfd_char_dev_major, 0));
 	class_unregister(&kfd_class);
 	unregister_chrdev(kfd_char_dev_major, kfd_dev_name);
+	kfd_fs_inode_free(kfd_anon_inode);
+	kfd_anon_inode = NULL;
 	kfd_device = NULL;
 }
 
@@ -150,12 +198,7 @@ static int kfd_open(struct inode *inode, struct file *filep)
 	if (iminor(inode) != 0)
 		return -ENODEV;
 
-	/*
-	 * /dev/kfd is a single chardev so all opens share one inode. Cache
-	 * its address_space on the first open for use by the reset path.
-	 */
-	if (!READ_ONCE(kfd_dev_mapping))
-		cmpxchg(&kfd_dev_mapping, NULL, inode->i_mapping);
+	filep->f_mapping = kfd_anon_inode->i_mapping;
 
 	is_32bit_user_mode = in_compat_syscall();
 
Signature · 1 line
2.55.0
author maintainer
#283 lore raw

PATCH 283/457 drm/amd/display: Fix dc stream excess put in dm_update_crtc_state()

In reply to Greg Kroah-Hartman

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

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

From: Wentao Liang <[email protected]>

commit c5fd4eaad50d620c7e09ac2082b2fb55ee54170e upstream.

In dm_update_crtc_state(), when a modeset is required the newly created stream is stored in dm_new_crtc_state->stream and an extra reference is taken with dc_stream_retain(). The reference returned by create_validate_stream_for_sink() is released as an extra reference at the skip_modeset label, leaving the stream owned by the new CRTC state.

If amdgpu_dm_check_crtc_color_mgmt() fails afterwards, the code jumps to the fail label which releases new_stream again. Since the extra reference was already released at skip_modeset, this drops the reference owned by dm_new_crtc_state->stream and the stream is released while the atomic state still points to it, leading to a premature free of the dc stream.

Set new_stream to NULL after releasing the extra reference at the skip_modeset label so that a later goto fail cannot release the reference owned by the new CRTC state.

Fixes 7cd4b70091a5 ("drm/amd/display: Rework CRTC color management")
(cherry picked from commit 102a47065a62dc8f6bbbb47cf082a2934282eb08)
---
 drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c |    4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +3 -1
@@ -12348,8 +12348,10 @@ static int dm_update_crtc_state(struct a
 
 skip_modeset:
 	/* Release extra reference */
-	if (new_stream)
+	if (new_stream) {
 		dc_stream_release(new_stream);
+		new_stream = NULL;
+	}
 	new_stream = NULL;
 
 	/*
author maintainer
#284 lore raw

PATCH 284/457 drm/amd/display: Bump frame warning limit for clang builds of dml

In reply to Greg Kroah-Hartman

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

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

From: Ivan Lipski <[email protected]>

commit 18779dd84515db093fedb4ebaf0998c9b165a5fb upstream.

[Why&How]
When building the DML files with clang without any sanitizer or LTO, the following -Wframe-larger-than errors break the build under CONFIG_WERROR:

display_mode_vba_30.c: error: stack frame size (2512) exceeds limit
(2048) in 'dml30_ModeSupportAndSystemConfigurationFull'
display_mode_vba_31.c: error: stack frame size (2416) exceeds limit
(2048) in 'dml31_ModeSupportAndSystemConfigurationFull'
display_mode_vba_314.c: error: stack frame size (2392) exceeds limit
(2048) in 'dml314_ModeSupportAndSystemConfigurationFull'

Clang consistently spills more than gcc, pushing the frame past the 2048 byte limit.

Apply an existing approach of increasing the warn stack size to the non-sanitizer path so plain clang builds use a 3072 byte limit.

(cherry picked from commit 21711b6e66bb7b41b1aec67b2d99aafe768c8fcb)
---
 drivers/gpu/drm/amd/display/dc/dml/Makefile |    6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)
drivers/gpu/drm/amd/display/dc/dml/Makefile +5 -1
@@ -36,7 +36,11 @@ ifneq ($(CONFIG_FRAME_WARN),0)
             frame_warn_limit := 3072
         endif
     else
-        frame_warn_limit := 2048
+        ifeq ($(CONFIG_CC_IS_CLANG),y)
+            frame_warn_limit := 3072
+        else
+            frame_warn_limit := 2048
+        endif
     endif
 
     ifeq ($(call test-lt, $(CONFIG_FRAME_WARN), $(frame_warn_limit)),y)
author maintainer
#285 lore raw

PATCH 285/457 drm/amdgpu/userq: fix double jiffies conversion in hang detect timeout

In reply to Greg Kroah-Hartman

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

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

From: Sunil Khatri <[email protected]>

commit cd195f1616b2bb5fb7765465326c4d5d64a620a0 upstream.

Function amdgpu_userq_start_hang_detect_work() calls msecs_to_jiffies() on adev->gfx_timeout/compute_timeout/sdma_timeout before arming hang_detect_work. These timeout values already hold jiffies values from amdgpu_device_get_job_timeout_settings() at device init.

This silently shrinks the real hang-detect deadline to (2 * HZ) ms instead of the intended timeout. e.g. 500ms instead of the 2000ms default on a CONFIG_HZ=250 kernel, only coincidentally correct at HZ=1000. The shortened window is easily exceeded by ordinary fence-completion latency, causing hang_detect_work to fire and trigger a per-queue or full GPU reset for queues that are not actually hung.

Pass the jiffies value directly to queue_delayed_work() instead of converting it a second time.

Fixes fc3336be9c62 ("drm/amd/amdgpu: Add independent hang detect work for user queue fence")
(cherry picked from commit 13d44ca033cb74756c2aef0ade54a75cdf2f6271)
---
 drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c |   12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)
drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c +6 -6
@@ -167,27 +167,27 @@ static void amdgpu_userq_hang_detect_wor
 void amdgpu_userq_start_hang_detect_work(struct amdgpu_usermode_queue *queue)
 {
 	struct amdgpu_device *adev;
-	unsigned long timeout_ms;
+	unsigned long timeout_jiffies;
 
 	adev = queue->userq_mgr->adev;
 	/* Determine timeout based on queue type */
 	switch (queue->queue_type) {
 	case AMDGPU_RING_TYPE_GFX:
-		timeout_ms = adev->gfx_timeout;
+		timeout_jiffies = adev->gfx_timeout;
 		break;
 	case AMDGPU_RING_TYPE_COMPUTE:
-		timeout_ms = adev->compute_timeout;
+		timeout_jiffies = adev->compute_timeout;
 		break;
 	case AMDGPU_RING_TYPE_SDMA:
-		timeout_ms = adev->sdma_timeout;
+		timeout_jiffies = adev->sdma_timeout;
 		break;
 	default:
-		timeout_ms = adev->gfx_timeout;
+		timeout_jiffies = adev->gfx_timeout;
 		break;
 	}
 
 	queue_delayed_work(adev->reset_domain->wq, &queue->hang_detect_work,
-			   msecs_to_jiffies(timeout_ms));
+			   timeout_jiffies);
 }
 
 void amdgpu_userq_process_fence_irq(struct amdgpu_device *adev, u32 doorbell)
author maintainer
#286 lore raw

PATCH 286/457 drm/amdgpu/vcn4.0.3: fix video_timeout unit mismatch in jpeg reset wait

In reply to Greg Kroah-Hartman

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

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

From: Sunil Khatri <[email protected]>

commit 6b13ddbf5bb8deec337f9b4887f579097a953f6a upstream.

vcn_v4_0_3_reset_jpeg_pre_helper() passes adev->video_timeout directly to amdgpu_fence_wait_polling(), whose timeout parameter is documented and implemented in usecs (busy-wait loop decrementing by udelay(2)).

adev->video_timeout is set in jiffies by amdgpu_device_get_job_timeout_settings(), via msecs_to_jiffies(). Passing it unconverted means the intended ~2s wait for outstanding JPEG fences to complete before the JPEG queue is torn down actually lasts only a couple of microseconds (HZ jiffies interpreted as usecs), so pending jobs are almost never given a real chance to finish before the reset path forces completion in the following helper.

Convert the jiffies value to usecs with jiffies_to_usecs() before passing it to amdgpu_fence_wait_polling().

Fixes d25c67fd9d6f ("drm/amdgpu/vcn4.0.3: rework reset handling")
Cc Jesse.Zhang <[email protected]>
Assisted-by Claude:claude-sonnet-5
(cherry picked from commit 5feabbd673c10ebee22b880e4d812f08974d2ef7)
---
 drivers/gpu/drm/amd/amdgpu/vcn_v4_0_3.c |    3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/gpu/drm/amd/amdgpu/vcn_v4_0_3.c +2 -1
@@ -1672,7 +1672,8 @@ static int vcn_v4_0_3_reset_jpeg_pre_hel
 		/* if Jobs are still pending after timeout,
 		 * We'll handle them in the bottom helper
 		 */
-		amdgpu_fence_wait_polling(ring, wait_seq, adev->video_timeout);
+		amdgpu_fence_wait_polling(ring, wait_seq,
+					  jiffies_to_usecs(adev->video_timeout));
        }
 
 	return 0;
author maintainer
#287 lore raw

PATCH 287/457 drm/amdgpu/vcn5.0.1: fix video_timeout unit mismatch in jpeg reset wait

In reply to Greg Kroah-Hartman

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

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

From: Sunil Khatri <[email protected]>

commit f952ed353a27b46c86c9525a39b7a642850b8139 upstream.

vcn_v5_0_1_reset_jpeg_pre_helper() passes adev->video_timeout directly to amdgpu_fence_wait_polling(), whose timeout parameter is documented and implemented in usecs (busy-wait loop decrementing by udelay(2)).

adev->video_timeout is set in jiffies by amdgpu_device_get_job_timeout_settings(), via msecs_to_jiffies(). Passing it unconverted means the intended ~2s wait for outstanding JPEG fences to complete before the JPEG queue is torn down actually lasts only a couple of microseconds (HZ jiffies interpreted as usecs), so pending jobs are almost never given a real chance to finish before the reset path forces completion in the following helper.

Convert the jiffies value to usecs with jiffies_to_usecs() before passing it to amdgpu_fence_wait_polling().

Fixes fab47d2db5ca ("drm/amdgpu/vcn5.0.1: rework reset handling")
Cc Jesse.Zhang <[email protected]>
Assisted-by Claude:claude-sonnet-5
(cherry picked from commit b8334fec8b90ebffcaa01001a23edca9f29a05e9)
---
 drivers/gpu/drm/amd/amdgpu/vcn_v5_0_1.c |    3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/gpu/drm/amd/amdgpu/vcn_v5_0_1.c +2 -1
@@ -1318,7 +1318,8 @@ static int vcn_v5_0_1_reset_jpeg_pre_hel
 		/* if Jobs are still pending after timeout,
 		 * We'll handle them in the bottom helper
 		 */
-		amdgpu_fence_wait_polling(ring, wait_seq, adev->video_timeout);
+		amdgpu_fence_wait_polling(ring, wait_seq,
+					  jiffies_to_usecs(adev->video_timeout));
        }
 
 	return 0;
author maintainer
#288 lore raw

PATCH 288/457 drm/amdgpu: Fix acpi device leak in amdgpu_acpi_enumerate_xcc()

In reply to Greg Kroah-Hartman

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

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

From: Wentao Liang <[email protected]>

commit a997baa61179b450bd55c4810c7ccfed3b753a54 upstream.

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)
---
 drivers/gpu/drm/amd/amdgpu/amdgpu_acpi.c |    4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/gpu/drm/amd/amdgpu/amdgpu_acpi.c +3 -1
@@ -1167,8 +1167,10 @@ int amdgpu_acpi_enumerate_xcc(void)
 		}
 
 		xcc_info = kzalloc_obj(struct amdgpu_acpi_xcc_info);
-		if (!xcc_info)
+		if (!xcc_info) {
+			acpi_dev_put(acpi_dev);
 			return -ENOMEM;
+		}
 
 		INIT_LIST_HEAD(&xcc_info->list);
 		xcc_info->handle = acpi_device_handle(acpi_dev);
author maintainer
#289 lore raw

PATCH 289/457 drm/amdgpu: Fix last_update fence leak in amdgpu_vm_init()

In reply to Greg Kroah-Hartman

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

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

From: Wentao Liang <[email protected]>

commit b4f7b4459b1b155e4c4977a6482b5df2cf08758c upstream.

amdgpu_vm_init() initializes vm->last_update, vm->last_unlocked and vm->last_tlb_flush with references to the stub fence taken via dma_fence_get_stub(). The error label at the end of the function releases the last_unlocked and last_tlb_flush references with dma_fence_put(), but the reference stored in vm->last_update is never dropped, so whenever the page table root creation, the reservation of the root BO or the PASID registration fails, the stub fence reference leaks.

Drop the vm->last_update reference together with the other stub fence references on the error path.

Fixes 187916e6ed9d ("drm/amdgpu: install stub fence into potential unused fence pointers")
(cherry picked from commit e7979c84fc05a176bdf855ee664871b1648404c9)
---
 drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c |    1 +
 1 file changed, 1 insertion(+)
drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c +1 -0
@@ -2665,6 +2665,7 @@ error_free_root:
 	amdgpu_bo_unref(&root_bo);
 
 error_free_delayed:
+	dma_fence_put(vm->last_update);
 	dma_fence_put(vm->last_tlb_flush);
 	dma_fence_put(vm->last_unlocked);
 	ttm_lru_bulk_move_fini(&adev->mman.bdev, &vm->lru_bulk_move);
author maintainer
#290 lore raw

PATCH 290/457 drm/amdgpu: Fix runtime PM leak in amdgpu_debugfs_test_ib_show()

In reply to Greg Kroah-Hartman

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

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

From: Wentao Liang <[email protected]>

commit 2b86ab1bd6673c525adda88819d7658ba9e784ec upstream.

amdgpu_debugfs_test_ib_show() resumes the device with pm_runtime_get_sync() before taking the reset domain semaphore with down_write_killable(). If the write lock acquisition is interrupted, the function returns without calling pm_runtime_put_autosuspend(), leaking the runtime PM reference acquired for the device and keeping the GPU awake.

Drop the runtime PM reference on the interrupted down_write_killable() error path before returning.

Fixes 6049db43d6dd ("drm/amdgpu: change reset lock from mutex to rw_semaphore")
(cherry picked from commit ec30a576c2d4c0364549e6c04218f50704ef56c8)
---
 drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c |    4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c +3 -1
@@ -1773,8 +1773,10 @@ static int amdgpu_debugfs_test_ib_show(s
 
 	/* Avoid accidently unparking the sched thread during GPU reset */
 	r = down_write_killable(&adev->reset_domain->sem);
-	if (r)
+	if (r) {
+		pm_runtime_put_autosuspend(dev->dev);
 		return r;
+	}
 
 	/* hold on the scheduler */
 	for (i = 0; i < AMDGPU_MAX_RINGS; i++) {
author maintainer
#291 lore raw

PATCH 291/457 drm/amdgpu: Fix vmid_wait fence leak in amdgpu_ring_init()

In reply to Greg Kroah-Hartman

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

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

From: Wentao Liang <[email protected]>

commit aea841bc62a76242396610d22d8ff40c13065f64 upstream.

amdgpu_ring_init() initializes ring->vmid_wait with a reference to the stub fence taken via dma_fence_get_stub(). When a later step of the initialization fails, e.g. amdgpu_fence_driver_init_ring(), a writeback slot allocation or the ring buffer allocation, the function returns an error without releasing the stub fence reference and the reference is leaked if the ring is torn down without amdgpu_ring_fini().

Move the stub fence assignment to the end of the initialization, right before the ring is registered with the GPU scheduler, where no further failure is possible. The stub fence is only consumed by command submission handling in amdgpu_ids.c once the ring is up and running, so nothing reads it during the error-prone part of the initialization.

Fixes 48e9fbd1a284 ("drm/amdgpu: initialize the vmid_wait with the stub fence")
(cherry picked from commit f2b96986851203e9c50ca0d13aaa3581ca3e8ebd)
---
 drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c +1 -1
@@ -254,7 +254,6 @@ int amdgpu_ring_init(struct amdgpu_devic
 		ring->adev = adev;
 		ring->num_hw_submission = sched_hw_submission;
 		ring->sched_score = sched_score;
-		ring->vmid_wait = dma_fence_get_stub();
 
 		ring->idx = adev->num_rings++;
 		adev->rings[ring->idx] = ring;
@@ -374,6 +373,7 @@ int amdgpu_ring_init(struct amdgpu_devic
 
 	ring->max_dw = max_dw;
 	ring->hw_prio = hw_prio;
+	ring->vmid_wait = dma_fence_get_stub();
 
 	if (!ring->no_scheduler && ring->funcs->type < AMDGPU_HW_IP_NUM) {
 		hw_ip = ring->funcs->type;
author maintainer
#292 lore raw

PATCH 292/457 drm/amdgpu: move userq fence wait out of signalling section

In reply to Greg Kroah-Hartman

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

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

From: Prike Liang <[email protected]>

commit 3022bdfe3e6d776e9273d6892f7c193138ca0666 upstream.

The eviction fence suspend worker waits for every pending userq fence from inside a dma_fence_begin_signalling() critical section. Waiting on another DMA fence while responsible for signalling one violates the cross-driver fence contract and is reported by lockdep as a dma_fence_map dependency.

Move the wait before dma_fence_begin_signalling(). Keep userq_mutex held so queue lifetime remains stable while inspecting last_fence.

Fixes fc61df151617 ("drm/amdgpu: annotate eviction fence signaling path")
(cherry picked from commit 3bd4fbc5ed89621340b5cd249869092691a9c81f)
---
 drivers/gpu/drm/amd/amdgpu/amdgpu_eviction_fence.c |    3 +++
 drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c          |    4 +---
 drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h          |    1 +
 3 files changed, 5 insertions(+), 3 deletions(-)
drivers/gpu/drm/amd/amdgpu/amdgpu_eviction_fence.c +3 -0
@@ -68,6 +68,9 @@ amdgpu_eviction_fence_suspend_worker(str
 
 	mutex_lock(&uq_mgr->userq_mutex);
 
+	/* Fence waits are not allowed in a fence signalling critical section. */
+	amdgpu_userq_wait_for_signal(uq_mgr);
+
 	/*
 	 * This is intentionally after taking the userq_mutex since we do
 	 * allocate memory while holding this lock, but only after ensuring that
drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c +1 -3
@@ -1168,7 +1168,7 @@ amdgpu_userq_evict_all(struct amdgpu_use
 	return ret;
 }
 
-static void
+void
 amdgpu_userq_wait_for_signal(struct amdgpu_userq_mgr *uq_mgr)
 {
 	struct amdgpu_usermode_queue *queue;
@@ -1187,8 +1187,6 @@ amdgpu_userq_wait_for_signal(struct amdg
 void
 amdgpu_userq_evict(struct amdgpu_userq_mgr *uq_mgr)
 {
-	/* Wait for any pending userqueue fence work to finish */
-	amdgpu_userq_wait_for_signal(uq_mgr);
 	amdgpu_userq_evict_all(uq_mgr);
 }
 
drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h +1 -0
@@ -156,6 +156,7 @@ void amdgpu_userq_mgr_cancel_reset_work(
 void amdgpu_userq_mgr_cancel_resume(struct amdgpu_userq_mgr *userq_mgr);
 void amdgpu_userq_mgr_fini(struct amdgpu_userq_mgr *userq_mgr);
 
+void amdgpu_userq_wait_for_signal(struct amdgpu_userq_mgr *uq_mgr);
 void amdgpu_userq_evict(struct amdgpu_userq_mgr *uq_mgr);
 
 void amdgpu_userq_ensure_ev_fence(struct amdgpu_userq_mgr *userq_mgr,
author maintainer
#293 lore raw

PATCH 293/457 drm/nouveau/dmem: pin VRAM for the whole registered range

In reply to Greg Kroah-Hartman

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

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

From: Junrui Luo <[email protected]>

commit 64ca4cdd1031206424e6455f0ae4fb0560d8f46a upstream.

Commit c32287471077 ("gpu/drm/nouveau: enable THP support for GPU memory migration") grew the device-private region that nouveau_dmem_chunk_alloc() registers from DMEM_CHUNK_SIZE to DMEM_CHUNK_SIZE * NR_CHUNKS, but left the VRAM buffer object backing that region at DMEM_CHUNK_SIZE.

nouveau_dmem_page_addr() returns chunk->bo->offset plus the page's offset within the registered region, so every page past the first chunk resolves to VRAM outside the buffer object.

Size the buffer object to the region it backs.

Fixes c32287471077 ("gpu/drm/nouveau: enable THP support for GPU memory migration")
Assisted-by LLM
---
 drivers/gpu/drm/nouveau/nouveau_dmem.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/gpu/drm/nouveau/nouveau_dmem.c +2 -2
@@ -339,8 +339,8 @@ nouveau_dmem_chunk_alloc(struct nouveau_
 	chunk->pagemap.ops = &nouveau_dmem_pagemap_ops;
 	chunk->pagemap.owner = drm->dev;
 
-	ret = nouveau_bo_new_pin(&drm->client, NOUVEAU_GEM_DOMAIN_VRAM, DMEM_CHUNK_SIZE,
-				 &chunk->bo);
+	ret = nouveau_bo_new_pin(&drm->client, NOUVEAU_GEM_DOMAIN_VRAM,
+				 DMEM_CHUNK_SIZE * NR_CHUNKS, &chunk->bo);
 	if (ret)
 		goto out_release;
 
author maintainer
#294 lore raw

PATCH 294/457 drm/nouveau/uvmm: fix UAF in nouveau_uvmm_sm when BO is in TTM_PL_SYSTEM

In reply to Greg Kroah-Hartman

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

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

From: Peiyang He <[email protected]>

commit 3359a372efb6d585c97019ee1b7f1874442bcebe upstream.

nouveau_uvmm_sm() calls op_map(), which passes bo->resource through nouveau_mem() to nouveau_uvma_map(). nouveau_uvmm_vmm_map() then reads mem->mem.type.

But this is only valid when bo->resource is backed by struct nouveau_mem, as is the case for VRAM and TT resources. If the BO is left in TTM_PL_SYSTEM, bo->resource is only a struct ttm_resource. Treating it as struct nouveau_mem makes the mem->mem.type read past the end of the resource, causing a KASAN: slab-use-after-free Read in nouveau_uvmm_sm report:

BUG: KASAN: slab-use-after-free in nouveau_uvmm_vmm_map drivers/gpu/drm/nouveau/nouveau_uvmm.c:152 [inline]
BUG: KASAN: slab-use-after-free in nouveau_uvma_map drivers/gpu/drm/nouveau/nouveau_uvmm.c:199 [inline]
BUG: KASAN: slab-use-after-free in op_map drivers/gpu/drm/nouveau/nouveau_uvmm.c:849 [inline]
BUG: KASAN: slab-use-after-free in nouveau_uvmm_sm.constprop.0+0x6ab/0x900 drivers/gpu/drm/nouveau/nouveau_uvmm.c:903
Read of size 1 at addr ffff888127d3e3a0 by task kworker/0:1/11

CPU: 0 UID: 0 PID: 11 Comm: kworker/0:1 Not tainted 7.2.0 #5 PREEMPT(lazy)
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
Workqueue: nouveau_sched_wq_2224 drm_sched_run_job_work
Call Trace:
 <TASK>
 __dump_stack lib/dump_stack.c:94 [inline]
 dump_stack_lvl+0x95/0xe0 lib/dump_stack.c:120
 print_address_description mm/kasan/report.c:378 [inline]
 print_report+0xcb/0x5a0 mm/kasan/report.c:482
 kasan_report+0xca/0x100 mm/kasan/report.c:595
 nouveau_uvmm_vmm_map drivers/gpu/drm/nouveau/nouveau_uvmm.c:152 [inline]
 nouveau_uvma_map drivers/gpu/drm/nouveau/nouveau_uvmm.c:199 [inline]
 op_map drivers/gpu/drm/nouveau/nouveau_uvmm.c:849 [inline]
 nouveau_uvmm_sm.constprop.0+0x6ab/0x900 drivers/gpu/drm/nouveau/nouveau_uvmm.c:903
 nouveau_uvmm_sm_unmap drivers/gpu/drm/nouveau/nouveau_uvmm.c:932 [inline]
 nouveau_uvmm_bind_job_run+0xd6/0x250 drivers/gpu/drm/nouveau/nouveau_uvmm.c:1532
 nouveau_job_run drivers/gpu/drm/nouveau/nouveau_sched.c:350 [inline]
 nouveau_sched_run_job+0x62/0xd0 drivers/gpu/drm/nouveau/nouveau_sched.c:364
 drm_sched_run_job_work+0x356/0xa10 drivers/gpu/drm/scheduler/sched_main.c:1061
 process_one_work+0x8a5/0x1900 kernel/workqueue.c:3322
 process_scheduled_works kernel/workqueue.c:3405 [inline]
 worker_thread+0x5dd/0xd80 kernel/workqueue.c:3486
 kthread+0x31d/0x420 kernel/kthread.c:436
 ret_from_fork+0x662/0x940 arch/x86/kernel/process.c:158
 ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245
 </TASK>

Allocated by task 2224 on cpu 0 at 66.550027s:
 kasan_save_stack+0x24/0x50 mm/kasan/common.c:57
 kasan_save_track+0x17/0x60 mm/kasan/common.c:78
 poison_kmalloc_redzone mm/kasan/common.c:398 [inline]
 __kasan_kmalloc+0xaa/0xb0 mm/kasan/common.c:415
 kasan_kmalloc include/linux/kasan.h:263 [inline]
 __do_kmalloc_node mm/slub.c:5334 [inline]
 __kmalloc_noprof+0x304/0x7c0 mm/slub.c:5359
 _kmalloc_noprof include/linux/slab.h:992 [inline]
 dma_resv_list_alloc+0x27/0x90 drivers/dma-buf/dma-resv.c:106
 dma_resv_reserve_fences+0x60e/0xa30 drivers/dma-buf/dma-resv.c:205
 ttm_bo_alloc_resource+0x12c/0xbd0 drivers/gpu/drm/ttm/ttm_bo.c:721
 ttm_bo_validate+0x1bc/0x4a0 drivers/gpu/drm/ttm/ttm_bo.c:856
 ttm_bo_init_reserved+0x2c3/0x570 drivers/gpu/drm/ttm/ttm_bo.c:970
 nouveau_bo_init+0x159/0x2c0 drivers/gpu/drm/nouveau/nouveau_bo.c:359
 nouveau_gem_new+0x234/0x5f0 drivers/gpu/drm/nouveau/nouveau_gem.c:272
 nouveau_gem_ioctl_new+0x1eb/0x420 drivers/gpu/drm/nouveau/nouveau_gem.c:352
 drm_ioctl_kernel+0x192/0x350 drivers/gpu/drm/drm_ioctl.c:817
 drm_ioctl+0x4f8/0xb40 drivers/gpu/drm/drm_ioctl.c:914
 nouveau_drm_ioctl+0xea/0x2c0 drivers/gpu/drm/nouveau/nouveau_drm.c:1338
 vfs_ioctl fs/ioctl.c:51 [inline]
 __do_sys_ioctl fs/ioctl.c:597 [inline]
 __se_sys_ioctl fs/ioctl.c:583 [inline]
 __x64_sys_ioctl+0x180/0x1d0 fs/ioctl.c:583
 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
 do_syscall_64+0x115/0x690 arch/x86/entry/syscall_64.c:94
 entry_SYSCALL_64_after_hwframe+0x77/0x7f

Freed by task 2223 on cpu 0 at 66.554063s:
 kasan_save_stack+0x24/0x50 mm/kasan/common.c:57
 kasan_save_track+0x17/0x60 mm/kasan/common.c:78
 kasan_save_free_info+0x3b/0x60 mm/kasan/generic.c:584
 poison_slab_object mm/kasan/common.c:253 [inline]
 __kasan_slab_free+0x61/0x80 mm/kasan/common.c:285
 kasan_slab_free include/linux/kasan.h:235 [inline]
 slab_free_hook mm/slub.c:2677 [inline]
 __rcu_free_sheaf_prepare+0xb6/0x2e0 mm/slub.c:2928
 rcu_free_sheaf+0x1b/0x120 mm/slub.c:5978
 rcu_do_batch kernel/rcu/tree.c:2645 [inline]
 rcu_core+0x521/0x1490 kernel/rcu/tree.c:2897
 handle_softirqs+0x1b1/0x8a0 kernel/softirq.c:622
 __do_softirq kernel/softirq.c:656 [inline]
 invoke_softirq kernel/softirq.c:496 [inline]
 __irq_exit_rcu+0x137/0x1c0 kernel/softirq.c:735
 irq_exit_rcu+0x9/0x20 kernel/softirq.c:752
 instr_sysvec_apic_timer_interrupt arch/x86/kernel/apic/apic.c:1062 [inline]
 sysvec_apic_timer_interrupt+0x70/0x80 arch/x86/kernel/apic/apic.c:1062
 asm_sysvec_apic_timer_interrupt+0x1a/0x20 arch/x86/include/asm/idtentry.h:674

The buggy address belongs to the object at ffff888127d3e380
 which belongs to the cache kmalloc-96 of size 96

The buggy address is located 32 bytes inside of
freed 96-byte region [ffff888127d3e380, ffff888127d3e3e0)

The buggy address belongs to the physical page:
page: refcount:0 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x127d3e
flags: 0x200000000000000(node=0|zone=2)
page_type: f5(slab)
raw: 0200000000000000 ffff888100041280 dead000000000122 0000000000000000
raw: 0000000000000000 0000000000200020 00000000f5000000 0000000000000000
page dumped because: kasan: bad access detected

Memory state around the buggy address:
 ffff888127d3e280: 00 00 00 00 00 00 00 00 00 fc fc fc fc fc fc fc
 ffff888127d3e300: 00 00 00 00 00 00 00 00 00 00 00 fc fc fc fc fc
>ffff888127d3e380: fa fb fb fb fb fb fb fb fb fb fb fb fc fc fc fc
                               ^
 ffff888127d3e400: fa fb fb fb fb fb fb fb fb fb fb fb fc fc fc fc
 ffff888127d3e480: 00 00 00 00 00 00 00 00 00 00 fc fc fc fc fc fc

Fix by resetting the placement to the BO's valid domains before calling nouveau_bo_validate(), matching the handling in nouveau_uvmm_bo_validate(), so map jobs do not run for SYSTEM resources; Reject BO that cannot reside in VRAM or GART;
Also skip op_map() when the GPUVA has been invalidated, matching the handling in the unmap and remap paths.

Found when fuzzing the nouveau driver with a modified Syzkaller.

---
 drivers/gpu/drm/nouveau/nouveau_uvmm.c |   13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)
drivers/gpu/drm/nouveau/nouveau_uvmm.c +11 -2
@@ -846,6 +846,9 @@ op_map(struct nouveau_uvma *uvma)
 {
 	struct nouveau_bo *nvbo = nouveau_gem_object(uvma->va.gem.obj);
 
+	if (drm_gpuva_invalidated(&uvma->va))
+		return;
+
 	nouveau_uvma_map(uvma, nouveau_mem(nvbo->bo.resource));
 }
 
@@ -1232,6 +1235,7 @@ bind_lock_validate(struct nouveau_job *j
 
 		drm_gpuva_for_each_op(va_op, op->ops) {
 			struct drm_gem_object *obj = op_gem_obj(va_op);
+			struct nouveau_bo *nvbo;
 
 			if (unlikely(!obj))
 				continue;
@@ -1246,8 +1250,13 @@ bind_lock_validate(struct nouveau_job *j
 			if (va_op->op == DRM_GPUVA_OP_UNMAP)
 				continue;
 
-			ret = nouveau_bo_validate(nouveau_gem_object(obj),
-						  true, false);
+			nvbo = nouveau_gem_object(obj);
+			if (!(nvbo->valid_domains &
+			      (NOUVEAU_GEM_DOMAIN_VRAM | NOUVEAU_GEM_DOMAIN_GART)))
+				return -EINVAL;
+
+			nouveau_bo_placement_set(nvbo, nvbo->valid_domains, 0);
+			ret = nouveau_bo_validate(nvbo, true, false);
 			if (ret)
 				return ret;
 		}
author maintainer
#295 lore raw

PATCH 295/457 drm/nouveau: fix autosuspend cleanup during teardown

In reply to Greg Kroah-Hartman

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

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

From: Guangshuo Li <[email protected]>

commit fefd9480ec361969f1a836df46326a1801062c26 upstream.

nouveau_drm_device_init() calls pm_runtime_use_autosuspend(), but nouveau_drm_device_fini() does not call the matching pm_runtime_dont_use_autosuspend().

If the autosuspend delay is set to a negative value while autosuspend is enabled, the runtime PM core increments usage_count to prevent runtime suspend. Without calling pm_runtime_dont_use_autosuspend() during teardown, this reference is not dropped and usage_count remains unbalanced.

The documentation for pm_runtime_use_autosuspend() also notes that it is important to undo it with pm_runtime_dont_use_autosuspend() at driver exit time, unless runtime PM was initially enabled with devm_pm_runtime_enable().

Add the missing pm_runtime_dont_use_autosuspend() call to the common device teardown path.

This issue was found by manual code inspection.

---
 drivers/gpu/drm/nouveau/nouveau_drm.c |    1 +
 1 file changed, 1 insertion(+)
drivers/gpu/drm/nouveau/nouveau_drm.c +1 -0
@@ -585,6 +585,7 @@ nouveau_drm_device_fini(struct nouveau_d
 	if (nouveau_pmops_runtime()) {
 		pm_runtime_get_sync(dev->dev);
 		pm_runtime_forbid(dev->dev);
+		pm_runtime_dont_use_autosuspend(dev->dev);
 	}
 
 	nouveau_led_fini(dev);
author maintainer
#296 lore raw

PATCH 296/457 drm/nouveau: Fix bridge reference leak in nv1a_ram_new()

In reply to Greg Kroah-Hartman

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

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

From: Wentao Liang <[email protected]>

commit 67b4411538c8341692548429d43256f25be99f7a upstream.

pci_get_domain_bus_and_slot() takes a reference to the PCI device, which is never released once the memory size has been read from its config space. Drop the reference before returning.

---
 drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv1a.c |    2 ++
 1 file changed, 2 insertions(+)
drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv1a.c +2 -0
@@ -51,6 +51,8 @@ nv1a_ram_new(struct nvkm_fb *fb, struct
 		mib = ((mem >> 4) & 127) + 1;
 	}
 
+	pci_dev_put(bridge);
+
 	return nvkm_ram_new_(&nv04_ram_func, fb, NVKM_RAM_TYPE_STOLEN,
 			     mib * 1024 * 1024, pram);
 }
author maintainer
#297 lore raw

PATCH 297/457 drm/nouveau: fix double-free in nvif_vmm_dtor

In reply to Greg Kroah-Hartman

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

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

From: Peiyang He <[email protected]>

commit 97077ac87afe9e91ec074ef0be64454e7ccbf344 upstream.

On failure, nouveau_cli_init() calls nouveau_cli_fini() to tear the client down. Then, nouveau_drm_open() also enters into its cleanup path and calls nouveau_cli_fini() AGAIN. nouveau_cli_fini() calls nouveau_vmm_fini():

	void
	nouveau_vmm_fini(struct nouveau_vmm *vmm)
	{
		nouveau_svmm_fini(&vmm->svmm);
		nvif_vmm_dtor(&vmm->vmm);
		vmm->cli = NULL;
	}

Inside nvif_vmm_dtor(), vmm->page is freed unconditionally:

	void
	nvif_vmm_dtor(struct nvif_vmm *vmm)
	{
		kfree(vmm->page);
		nvif_object_dtor(&vmm->object);
	}

vmm->page is never cleared after being freed, so the second call of nvif_vmm_dtor() will cause a double-free.

Found by fuzzing the nouveau driver with a modified Syzkaller:

	BUG: KASAN: double-free in nvif_vmm_dtor+0x31/0x50 drivers/gpu/drm/nouveau/nvif/vmm.c:194
	Free of addr ffff888010fcdc30 by task syz.0.173/2567

	CPU: 1 UID: 0 PID: 2567 Comm: syz.0.173 Not tainted 7.2.0 #24 PREEMPT(lazy)
	Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
	Call Trace:
	<TASK>
	__dump_stack lib/dump_stack.c:94 [inline]
	dump_stack_lvl+0x95/0xe0 lib/dump_stack.c:120
	print_address_description mm/kasan/report.c:378 [inline]
	print_report+0xcb/0x5a0 mm/kasan/report.c:482
	kasan_report_invalid_free+0xaa/0xd0 mm/kasan/report.c:557
	check_slab_allocation+0xe4/0x110 mm/kasan/common.c:235
	kasan_slab_pre_free include/linux/kasan.h:199 [inline]
	slab_free_hook mm/slub.c:2622 [inline]
	slab_free mm/slub.c:6377 [inline]
	kfree+0x192/0x590 mm/slub.c:6692
	nvif_vmm_dtor+0x31/0x50 drivers/gpu/drm/nouveau/nvif/vmm.c:194
	nouveau_vmm_fini+0x16/0x50 drivers/gpu/drm/nouveau/nouveau_vmm.c:127
	nouveau_cli_fini+0x10e/0x210 drivers/gpu/drm/nouveau/nouveau_drm.c:225
	nouveau_drm_open+0x24e/0x740 drivers/gpu/drm/nouveau/nouveau_drm.c:1255
	drm_file_alloc+0x5f2/0xad0 drivers/gpu/drm/drm_file.c:176
	drm_open_helper+0x1d7/0x4a0 drivers/gpu/drm/drm_file.c:335
	drm_open+0x190/0x3d0 drivers/gpu/drm/drm_file.c:388
	drm_stub_open+0x1f2/0x460 drivers/gpu/drm/drm_drv.c:1211
	chrdev_open+0x21c/0x660 fs/char_dev.c:411
	do_dentry_open+0x59d/0x12b0 fs/open.c:947
	vfs_open+0x82/0x390 fs/open.c:1052
	do_open fs/namei.c:4700 [inline]
	path_openat+0x2345/0x3420 fs/namei.c:4863
	do_file_open+0x207/0x460 fs/namei.c:4892
	do_sys_openat2+0xd1/0x1d0 fs/open.c:1368
	do_sys_open fs/open.c:1374 [inline]
	__do_sys_openat fs/open.c:1390 [inline]
	__se_sys_openat fs/open.c:1385 [inline]
	__x64_sys_openat+0x144/0x200 fs/open.c:1385
	do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
	do_syscall_64+0x115/0x690 arch/x86/entry/syscall_64.c:94
	entry_SYSCALL_64_after_hwframe+0x77/0x7f
	RIP: 0033:0x7fc6d687594d
	Code: ff c3 66 2e 0f 1f 84 00 00 00 00 00 90 f3 0f 1e fa 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 b0 ff ff ff f7 d8 64 89 01 48
	RSP: 002b:00007fc6d5295008 EFLAGS: 00000246 ORIG_RAX: 0000000000000101
	RAX: ffffffffffffffda RBX: 00007fc6d6b06180 RCX: 00007fc6d687594d
	RDX: 0000000000022501 RSI: 0000200000000000 RDI: ffffffffffffff9c
	RBP: 00007fc6d691c303 R08: 0000000000000000 R09: 0000000000000000
	R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
	R13: 00007fc6d6b06218 R14: 00007fc6d6b06180 R15: 00007ffd9451d760
	</TASK>

	Allocated by task 2567 on cpu 1 at 163.593900s:
	kasan_save_stack+0x24/0x50 mm/kasan/common.c:57
	kasan_save_track+0x17/0x60 mm/kasan/common.c:78
	poison_kmalloc_redzone mm/kasan/common.c:398 [inline]
	__kasan_kmalloc+0xaa/0xb0 mm/kasan/common.c:415
	kasan_kmalloc include/linux/kasan.h:263 [inline]
	__do_kmalloc_node mm/slub.c:5334 [inline]
	__kmalloc_noprof+0x304/0x7c0 mm/slub.c:5359
	_kmalloc_noprof include/linux/slab.h:992 [inline]
	nvif_vmm_ctor+0x3c0/0x7e0 drivers/gpu/drm/nouveau/nvif/vmm.c:237
	nouveau_vmm_init+0x40/0x90 drivers/gpu/drm/nouveau/nouveau_vmm.c:134
	nouveau_cli_init+0x7b9/0xe10 drivers/gpu/drm/nouveau/nouveau_drm.c:293
	nouveau_drm_open+0x236/0x740 drivers/gpu/drm/nouveau/nouveau_drm.c:1243
	drm_file_alloc+0x5f2/0xad0 drivers/gpu/drm/drm_file.c:176
	drm_open_helper+0x1d7/0x4a0 drivers/gpu/drm/drm_file.c:335
	drm_open+0x190/0x3d0 drivers/gpu/drm/drm_file.c:388
	drm_stub_open+0x1f2/0x460 drivers/gpu/drm/drm_drv.c:1211
	chrdev_open+0x21c/0x660 fs/char_dev.c:411
	do_dentry_open+0x59d/0x12b0 fs/open.c:947
	vfs_open+0x82/0x390 fs/open.c:1052
	do_open fs/namei.c:4700 [inline]
	path_openat+0x2345/0x3420 fs/namei.c:4863
	do_file_open+0x207/0x460 fs/namei.c:4892
	do_sys_openat2+0xd1/0x1d0 fs/open.c:1368
	do_sys_open fs/open.c:1374 [inline]
	__do_sys_openat fs/open.c:1390 [inline]
	__se_sys_openat fs/open.c:1385 [inline]
	__x64_sys_openat+0x144/0x200 fs/open.c:1385
	do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
	do_syscall_64+0x115/0x690 arch/x86/entry/syscall_64.c:94
	entry_SYSCALL_64_after_hwframe+0x77/0x7f

	Freed by task 2567 on cpu 1 at 163.601355s:
	kasan_save_stack+0x24/0x50 mm/kasan/common.c:57
	kasan_save_track+0x17/0x60 mm/kasan/common.c:78
	kasan_save_free_info+0x3b/0x60 mm/kasan/generic.c:584
	poison_slab_object mm/kasan/common.c:253 [inline]
	__kasan_slab_free+0x61/0x80 mm/kasan/common.c:285
	kasan_slab_free include/linux/kasan.h:235 [inline]
	slab_free_hook mm/slub.c:2677 [inline]
	slab_free mm/slub.c:6377 [inline]
	kfree+0x383/0x590 mm/slub.c:6692
	nvif_vmm_dtor+0x31/0x50 drivers/gpu/drm/nouveau/nvif/vmm.c:194
	nouveau_vmm_fini+0x16/0x50 drivers/gpu/drm/nouveau/nouveau_vmm.c:127
	nouveau_cli_fini+0x10e/0x210 drivers/gpu/drm/nouveau/nouveau_drm.c:225
	nouveau_cli_init+0x593/0xe10 drivers/gpu/drm/nouveau/nouveau_drm.c:324
	nouveau_drm_open+0x236/0x740 drivers/gpu/drm/nouveau/nouveau_drm.c:1243
	drm_file_alloc+0x5f2/0xad0 drivers/gpu/drm/drm_file.c:176
	drm_open_helper+0x1d7/0x4a0 drivers/gpu/drm/drm_file.c:335
	drm_open+0x190/0x3d0 drivers/gpu/drm/drm_file.c:388
	drm_stub_open+0x1f2/0x460 drivers/gpu/drm/drm_drv.c:1211
	chrdev_open+0x21c/0x660 fs/char_dev.c:411
	do_dentry_open+0x59d/0x12b0 fs/open.c:947
	vfs_open+0x82/0x390 fs/open.c:1052
	do_open fs/namei.c:4700 [inline]
	path_openat+0x2345/0x3420 fs/namei.c:4863
	do_file_open+0x207/0x460 fs/namei.c:4892
	do_sys_openat2+0xd1/0x1d0 fs/open.c:1368
	do_sys_open fs/open.c:1374 [inline]
	__do_sys_openat fs/open.c:1390 [inline]
	__se_sys_openat fs/open.c:1385 [inline]
	__x64_sys_openat+0x144/0x200 fs/open.c:1385
	do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
	do_syscall_64+0x115/0x690 arch/x86/entry/syscall_64.c:94
	entry_SYSCALL_64_after_hwframe+0x77/0x7f

	The buggy address belongs to the object at ffff888010fcdc30
	which belongs to the cache kmalloc-16 of size 16
	The buggy address is located 0 bytes inside of
	16-byte region [ffff888010fcdc30, ffff888010fcdc40)

	The buggy address belongs to the physical page:
	page: refcount:0 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x10fcd
	flags: 0x100000000000000(node=0|zone=1)
	page_type: f5(slab)
	raw: 0100000000000000 ffff88800d441640 dead000000000100 dead000000000122
	raw: 0000000000000000 0000000000550055 00000000f5000000 0000000000000000
	page dumped because: kasan: bad access detected

	Memory state around the buggy address:
	ffff888010fcdb00: fc fc 00 04 fc fc fc fc fa fb fc fc fc fc fa fb
	ffff888010fcdb80: fc fc fc fc fa fb fc fc fc fc 00 07 fc fc fc fc
	>ffff888010fcdc00: fa fb fc fc fc fc fa fb fc fc fc fc fa fb fc fc
										^
	ffff888010fcdc80: fc fc fa fb fc fc fc fc 00 04 fc fc fc fc fa fb
	ffff888010fcdd00: fc fc fc fc 00 00 fc fc fc fc fa fb fc fc fc fc

Fix by removing the redundant teardown in nouveau_drm_open(), since nouveau_cli_init() already does the cleanup work. Also clear vmm->page after its freeing.

---
 drivers/gpu/drm/nouveau/nouveau_drm.c |    4 +---
 drivers/gpu/drm/nouveau/nvif/vmm.c    |    1 +
 2 files changed, 2 insertions(+), 3 deletions(-)
drivers/gpu/drm/nouveau/nouveau_drm.c +1 -3
@@ -1252,10 +1252,8 @@ nouveau_drm_open(struct drm_device *dev,
 	mutex_unlock(&drm->clients_lock);
 
 done:
-	if (ret && cli) {
-		nouveau_cli_fini(cli);
+	if (ret && cli)
 		kfree(cli);
-	}
 
 	pm_runtime_mark_last_busy(dev->dev);
 	pm_runtime_put_autosuspend(dev->dev);
drivers/gpu/drm/nouveau/nvif/vmm.c +1 -0
@@ -192,6 +192,7 @@ void
 nvif_vmm_dtor(struct nvif_vmm *vmm)
 {
 	kfree(vmm->page);
+	vmm->page = NULL;
 	nvif_object_dtor(&vmm->object);
 }
 
author maintainer
#298 lore raw

PATCH 298/457 drm/nouveau: Fix gem reference leak in validate_init()

In reply to Greg Kroah-Hartman

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

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

From: Wentao Liang <[email protected]>

commit 5ea72f7b7139b123713a7983448f910bc4514d9e upstream.

On the ttm_bo_reserve() failure and "vma not found" error paths, the loop breaks without adding the looked-up object to any validate list, so the reference taken by drm_gem_object_lookup() is never released; validate_fini() only walks the spliced lists. Drop the reference before breaking out on both paths.

Fixes 19ca10d82e33bcfe ("drm/nouveau/gem: lookup VMAs for buffers referenced by pushbuf ioctl")
---
 drivers/gpu/drm/nouveau/nouveau_gem.c |    2 ++
 1 file changed, 2 insertions(+)
drivers/gpu/drm/nouveau/nouveau_gem.c +2 -0
@@ -522,6 +522,7 @@ retry:
 			if (unlikely(ret)) {
 				if (ret != -ERESTARTSYS)
 					NV_PRINTK(err, cli, "fail reserve\n");
+				drm_gem_object_put(gem);
 				break;
 			}
 		}
@@ -531,6 +532,7 @@ retry:
 			struct nouveau_vma *vma = nouveau_vma_find(nvbo, vmm);
 			if (!vma) {
 				NV_PRINTK(err, cli, "vma not found!\n");
+				drm_gem_object_put(gem);
 				ret = -EINVAL;
 				break;
 			}
author maintainer
#299 lore raw

PATCH 299/457 drm/nouveau: Fix runtime PM leak in nouveau_connector_detect()

In reply to Greg Kroah-Hartman

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

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

From: Wentao Liang <[email protected]>

commit 1e04611d3735543bd80a67d9d13dc13f503746fb upstream.

If nvif_outp_edid_get() fails, nouveau_connector_detect() returns early without dropping the runtime PM reference taken at the start of the function, keeping the device powered on until the next successful detect.

Balance the reference on the error path like the other exit paths do.

---
 drivers/gpu/drm/nouveau/nouveau_connector.c |    5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)
drivers/gpu/drm/nouveau/nouveau_connector.c +4 -1
@@ -600,8 +600,11 @@ nouveau_connector_detect(struct drm_conn
 				new_edid = drm_get_edid(connector, nv_encoder->i2c);
 		} else {
 			ret = nvif_outp_edid_get(&nv_encoder->outp, (u8 **)&new_edid);
-			if (ret < 0)
+			if (ret < 0) {
+				pm_runtime_mark_last_busy(dev->dev);
+				pm_runtime_put_autosuspend(dev->dev);
 				return connector_status_disconnected;
+			}
 		}
 
 		nouveau_connector_set_edid(nv_connector, new_edid);
author maintainer
#300 lore raw

PATCH 300/457 drm/nouveau: RCU-free the scheduler-containing nouveau_sched

In reply to Greg Kroah-Hartman

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

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

From: Jonghyuk Kim(MalHyuk) <[email protected]>

commit f7eae6d8d768fabd6b59779ca7da79e02c74e113 upstream.

struct nouveau_sched embeds a struct drm_gpu_scheduler (base). nouveau_sched_destroy() calls nouveau_sched_fini() (which does drm_sched_fini(&sched->base)) and then frees the object with plain kfree(sched).

drm_sched_fence_get_timeline_name() returns fence->sched->name, and the scheduler fence keeps a .release callback so it is not ops-detached on signalling. A finished fence exported to userspace via drm_syncobj / sync_file therefore keeps pointing at &sched->base after nouveau_sched_destroy(), and a later get_timeline_name() -- reachable unprivileged through SYNC_IOC_FILE_INFO -- dereferences freed memory (KASAN slab-use-after-free read).

Per the dma-fence lifetime contract the exporter must keep the data backing a signalled fence alive for an RCU grace period. Free the scheduler-containing object with kfree_rcu() instead of kfree().

Fixes 5f03a507b29e ("drm/nouveau: implement 1:1 scheduler - entity relationship")
---
 drivers/gpu/drm/nouveau/nouveau_sched.c |    2 +-
 drivers/gpu/drm/nouveau/nouveau_sched.h |    1 +
 2 files changed, 2 insertions(+), 1 deletion(-)
drivers/gpu/drm/nouveau/nouveau_sched.c +1 -1
@@ -517,7 +517,7 @@ nouveau_sched_destroy(struct nouveau_sch
 	struct nouveau_sched *sched = *psched;
 
 	nouveau_sched_fini(sched);
-	kfree(sched);
+	kfree_rcu(sched, rcu);
 
 	*psched = NULL;
 }
drivers/gpu/drm/nouveau/nouveau_sched.h +1 -0
@@ -98,6 +98,7 @@ void nouveau_job_free(struct nouveau_job
 
 struct nouveau_sched {
 	struct drm_gpu_scheduler base;
+	struct rcu_head rcu;
 	struct drm_sched_entity entity;
 	struct workqueue_struct *wq;
 	struct mutex mutex;
author maintainer
#301 lore raw

PATCH 301/457 drm/nouveau: dont bump pin count on failed re-pin in nouveau_bo_pin_locked()

In reply to Greg Kroah-Hartman

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

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

From: Peiyang He <[email protected]>

commit 6a6870d3077faa501ca97760057ddca22b68418d upstream.

nouveau_bo_pin_locked() checks whether an already pinned BO is in a memory domain compatible with a new pin request. When the domains are incompatible, it sets -EBUSY but still calls ttm_bo_pin() before returning.

Callers treat a failed nouveau_bo_pin() as not having acquired a new pin, so the extra pin count is never decreased by a matching unpin. This triggers the warning in ttm_bo_release():

	WARN_ON_ONCE(bo->pin_count);

Found when fuzzing the nouveau driver with a modified Syzkaller:

	WARNING: drivers/gpu/drm/ttm/ttm_bo.c:256 at ttm_bo_release+0x827/0x9e0 drivers/gpu/drm/ttm/ttm_bo.c:256, CPU#1: syz.3.24/2212
	Modules linked in:
	CPU: 1 UID: 0 PID: 2212 Comm: syz.3.24 Not tainted 7.2.0 #24 PREEMPT(lazy)
	nouveau 0000:01:00.0: gsp:msg fn:103 len:0x40/0x20 res:0x19 resp:0x19
	Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
	RIP: 0010:ttm_bo_release+0x827/0x9e0 drivers/gpu/drm/ttm/ttm_bo.c:256
	Code: 02 00 0f 85 51 01 00 00 48 8b 7b 08 e8 d2 20 01 00 e9 80 fd ff ff e8 d8 15 c0 fe 90 0f 0b 90 e9 e1 f8 ff ff e8 ca 15 c0 fe 90 <0f> 0b 90 e9 a4 f8 ff ff e8 bc 15 c0 fe be 03 00 00 00 4c 89 e7 e8
	msg: 00000000: 05 00 d0 c1 04 00 f0 f1 01 30 00 00 2d 90 00 00  .........0..-...
	RSP: 0018:ffffc9000f5cf710 EFLAGS: 00010293
	RAX: 0000000000000000 RBX: ffff888018e5d2a8 RCX: ffffffff82bb1b36
	RDX: ffff888017b68000 RSI: 0000000000000004 RDI: ffff888018e5d2a8
	msg: 00000010: 19 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
	RBP: ffff88801261c720 R08: 0000000000000001 R09: ffffed10031cba55
	R10: ffff888018e5d2ab R11: 00000000000000f3 R12: ffff888018e5d290
	R13: ffff888018e5d2d4 R14: ffff88801b219c18 R15: dffffc0000000000
	FS:  0000000000000000(0000) GS:ffff8880e0f6f000(0000) knlGS:0000000000000000
	CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
	CR2: 0000001b31223ffc CR3: 0000000028e00005 CR4: 0000000000770ef0
	PKRU: 80000000
	Call Trace:
	<TASK>
	kref_put include/linux/kref.h:65 [inline]
	ttm_bo_put drivers/gpu/drm/ttm/ttm_bo.c:325 [inline]
	ttm_bo_fini+0x55/0x80 drivers/gpu/drm/ttm/ttm_bo.c:330
	nouveau_gem_object_del+0xb2/0x1b0 drivers/gpu/drm/nouveau/nouveau_gem.c:90
	drm_gem_object_free+0x5f/0x90 drivers/gpu/drm/drm_gem.c:1165
	kref_put include/linux/kref.h:65 [inline]
	__drm_gem_object_put include/drm/drm_gem.h:562 [inline]
	drm_gem_object_put include/drm/drm_gem.h:575 [inline]
	nouveau_abi16_chan_fini.constprop.0+0x44f/0x5a0 drivers/gpu/drm/nouveau/nouveau_abi16.c:195
	nouveau 0000:01:00.0: syz.2.23[2209]: Unknown handle 0x00000000
	nouveau_abi16_fini+0x1d0/0x340 drivers/gpu/drm/nouveau/nouveau_abi16.c:225
	nouveau_drm_postclose+0x18b/0x3e0 drivers/gpu/drm/nouveau/nouveau_drm.c:1284
	nouveau 0000:01:00.0: syz.2.23[2209]: validate_init
	drm_file_free.part.0+0x6d6/0xb60 drivers/gpu/drm/drm_file.c:267
	drm_file_free drivers/gpu/drm/drm_file.c:237 [inline]
	drm_close_helper.isra.0+0x11a/0x160 drivers/gpu/drm/drm_file.c:290
	drm_release+0x1ab/0x330 drivers/gpu/drm/drm_file.c:438
	__fput+0x39c/0xa60 fs/file_table.c:512
	nouveau 0000:01:00.0: syz.2.23[2209]: validate: -2
	task_work_run+0x15a/0x230 kernel/task_work.c:233
	exit_task_work include/linux/task_work.h:40 [inline]
	do_exit+0x82b/0x25a0 kernel/exit.c:1009
	do_group_exit+0xc2/0x280 kernel/exit.c:1152
	get_signal+0x1d6e/0x1f30 kernel/signal.c:3046
	arch_do_signal_or_restart+0x7d/0x6e0 arch/x86/kernel/signal.c:337
	__exit_to_user_mode_loop kernel/entry/common.c:66 [inline]
	exit_to_user_mode_loop+0xdf/0x440 kernel/entry/common.c:101
	__exit_to_user_mode_prepare include/linux/irq-entry-common.h:207 [inline]
	syscall_exit_to_user_mode_prepare include/linux/irq-entry-common.h:230 [inline]
	syscall_exit_to_user_mode include/linux/entry-common.h:318 [inline]
	do_syscall_64+0x4f8/0x690 arch/x86/entry/syscall_64.c:100
	entry_SYSCALL_64_after_hwframe+0x77/0x7f
	RIP: 0033:0x7f12bac8594d
	Code: Unable to access opcode bytes at 0x7f12bac85923.
	RSP: 002b:00007f12b96e70d8 EFLAGS: 00000246 ORIG_RAX: 00000000000000ca
	RAX: 0000000000000001 RBX: 00007f12baf15fa8 RCX: 00007f12bac8594d
	RDX: 00000000000f4240 RSI: 0000000000000081 RDI: 00007f12baf15fac
	RBP: 00007f12baf15fa0 R08: 00007f12baee8000 R09: 0000000000000000
	R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
	R13: 00007f12baf16038 R14: 0000000000000006 R15: 00007ffe2ed394b0
	</TASK>
	irq event stamp: 47867
	hardirqs last  enabled at (47883): [<ffffffff815cafc6>] __up_console_sem+0x66/0x70 kernel/printk/printk.c:347
	hardirqs last disabled at (47892): [<ffffffff815cafab>] __up_console_sem+0x4b/0x70 kernel/printk/printk.c:345
	softirqs last  enabled at (47880): [<ffffffff81434277>] __do_softirq kernel/softirq.c:656 [inline]
	softirqs last  enabled at (47880): [<ffffffff81434277>] invoke_softirq kernel/softirq.c:496 [inline]
	softirqs last  enabled at (47880): [<ffffffff81434277>] __irq_exit_rcu+0x137/0x1c0 kernel/softirq.c:735
	softirqs last disabled at (47875): [<ffffffff81434277>] __do_softirq kernel/softirq.c:656 [inline]
	softirqs last disabled at (47875): [<ffffffff81434277>] invoke_softirq kernel/softirq.c:496 [inline]
	softirqs last disabled at (47875): [<ffffffff81434277>] __irq_exit_rcu+0x137/0x1c0 kernel/softirq.c:735

Fix by calling ttm_bo_pin() only when the existing placement is compatible with the new pin request. This matches the correct behavior in other DRM drivers such as amdgpu_bo_pin() in amdgpu.

Fixes ad76b3f7c7a0 ("drm/nouveau: teach nouveau_bo_pin() how to force a contig vram allocation")
Assisted-by LLM
---
 drivers/gpu/drm/nouveau/nouveau_bo.c |    3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/gpu/drm/nouveau/nouveau_bo.c +2 -1
@@ -578,8 +578,9 @@ int nouveau_bo_pin_locked(struct nouveau
 				      "0x%08x vs 0x%08x\n", bo,
 				 bo->resource->mem_type, domain);
 			ret = -EBUSY;
+		} else {
+			ttm_bo_pin(&nvbo->bo);
 		}
-		ttm_bo_pin(&nvbo->bo);
 		goto out;
 	}
 
author maintainer
#302 lore raw

PATCH 302/457 drm/xe/vm: nuke PTs only after unlinking contested VMAs

In reply to Greg Kroah-Hartman

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

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

From: Matthew Auld <[email protected]>

commit 24a22fb3c731474b68e986af6804db450fb88617 upstream.

In xe_vm_close_and_put(), external-BO VMAs are queued on the contested list for deferred destruction via xe_vma_destroy_unlocked(). However, xe_vm_pt_destroy() was previously invoked before processing contested VMAs, destroying vm->pt_root while those VMAs were still linked to their respective buffer objects (vm_bo->list.gpuva).

If a concurrent thread evicts one of those shared buffer objects, xe_bo_trigger_rebind() holding only bo->resv walks the BO's VMAs and, in fault mode, calls xe_vm_invalidate_vma() -> xe_pt_zap_ptes(). Because vm->pt_root[tile->id] is already NULL, dereferencing pt->level causes a NULL ptr deref.

Fix this by deferring xe_vm_free_scratch() and xe_vm_pt_destroy() until after all contested VMAs have been unlinked and destroyed.

User is reporting hitting a NULL ptr deref in xe_pt_zap_ptes(), which could be explained by this race.

Assisted-by LLM
Fixes b06d47be7c83 ("drm/xe: Port Xe to GPUVA")
Cc Thomas Hellström <[email protected]>
Cc Matthew Brost <[email protected]>
Cc <[email protected]> # v6.12+
(cherry picked from commit c2863648959489767f08892fd6e90577d2ea0b6a)
---
 drivers/gpu/drm/xe/xe_vm.c |   21 +++++++++------------
 1 file changed, 9 insertions(+), 12 deletions(-)
drivers/gpu/drm/xe/xe_vm.c +9 -12
@@ -1933,21 +1933,13 @@ void xe_vm_close_and_put(struct xe_vm *v
 		vma->gpuva.flags |= XE_VMA_DESTROYED;
 	}
 
-	/*
-	 * All vm operations will add shared fences to resv.
-	 * The only exception is eviction for a shared object,
-	 * but even so, the unbind when evicted would still
-	 * install a fence to resv. Hence it's safe to
-	 * destroy the pagetables immediately.
-	 */
-	xe_vm_free_scratch(vm);
-	xe_vm_pt_destroy(vm);
 	xe_vm_unlock(vm);
 
 	/*
-	 * VM is now dead, cannot re-add nodes to vm->vmas if it's NULL
-	 * Since we hold a refcount to the bo, we can remove and free
-	 * the members safely without locking.
+	 * Unlink and destroy all contested external-BO VMAs before destroying
+	 * the page tables. Otherwise, concurrent eviction holding only bo->resv
+	 * can walk the BO's VMAs and attempt to invalidate/zap page tables that
+	 * have already been freed.
 	 */
 	list_for_each_entry_safe(vma, next_vma, &contested,
 				 combined_links.destroy) {
@@ -1955,6 +1947,11 @@ void xe_vm_close_and_put(struct xe_vm *v
 		xe_vma_destroy_unlocked(vma);
 	}
 
+	xe_vm_lock(vm, false);
+	xe_vm_free_scratch(vm);
+	xe_vm_pt_destroy(vm);
+	xe_vm_unlock(vm);
+
 	xe_svm_fini(vm);
 
 	up_write(&vm->lock);
author maintainer
#303 lore raw

PATCH 303/457 drm/xe: harden adjust_idledly() against divide-by-zero and overflow

In reply to Greg Kroah-Hartman

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

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

From: Tangudu Tilak Tirumalesh <[email protected]>

commit 90f467577ebc28c5bc4ba2f0f1b83a4419fb0740 upstream.

adjust_idledly() has several corner-case issues flagged during review:

1. If xe_gt_clock_init() failed to recognise the crystal clock, gt->info.timestamp_base is 0, which makes idledly_units_ps also 0. The subsequent DIV_ROUND_CLOSEST(..., idledly_units_ps) is then a divide-by-zero and panics the kernel.

2. The tick-to-ns conversions are done in u32:
idledly * idledly_units_ps, (maxcnt - 1) * 1000
Both overflow u32 before DIV_ROUND_CLOSEST() sees them.

3. If IDLE_WAIT_TIME reads back as 0, maxcnt evaluates to 0 and

     the maxcnt - 1 clamp wraps to 0xFFFFFFFF in u32.

4. The register only stores whole ticks, so the clamped ns value has to be converted to ticks and back. DIV_ROUND_CLOSEST() can round that conversion up past maxcnt:

maxcnt = 640 ns, one tick = 666664 ps

clamp: maxcnt - 1 = 639 ns
ns -> ticks: 639000 / 666664 = 0.958 -> rounds to 1 tick
tick -> ns: 1 * 666664 / 1000 = 667 ns

667 ns is programmed into RING_IDLEDLY, but 667 >= maxcnt (640), so xe_gt_WARN_ON() fires again on every subsequent init.

Return early if timestamp_base is 0 (the unknown-crystal path). Do the conversions in u64 via the *_ULL() helpers so they cannot wrap. Clamp with a floor (DIV_ROUND_DOWN_ULL) so the programmed delay stays strictly below maxcnt, and guard the maxcnt == 0 case with a zero delay while still writing RING_IDLEDLY so INHIBIT_SWITCH_UNTIL_PREEMPTED is cleared.

v2: Drop the redundant warn on the timestamp_base == 0 path;
xe_gt_clock_init() already warns on an unrecognised crystal clock. Keep the early return to avoid the divide-by-zero. - Vinay

v3: Field-mask the RING_IDLEDLY write with REG_FIELD_PREP(IDLE_DELAY, ...)
instead of writing the raw tick count, which could clobber INHIBIT_SWITCH_UNTIL_PREEMPTED and reserved bits. Split the inhibit-switch clear from the maxcnt clamp so a set inhibit bit no longer forces a needless delay overwrite when the delay itself is already valid. Use gt_to_xe(gt) instead of gt_to_xe(hwe->gt).

Fixes d2de4410a88f ("drm/xe: Apply Wa_16023105232")
Assisted-by GitHub_Copilot:claude-opus-4.8
(cherry picked from commit d864065ea25e9d12897c175de9176ce46677e176)
---
 drivers/gpu/drm/xe/xe_hw_engine.c |   27 +++++++++++++++++++++------
 1 file changed, 21 insertions(+), 6 deletions(-)
drivers/gpu/drm/xe/xe_hw_engine.c +21 -6
@@ -584,22 +584,37 @@ static void adjust_idledly(struct xe_hw_
 	u32 idledly_units_ps = 8 * gt->info.timestamp_base;
 	u32 maxcnt_units_ns = 640;
 	bool inhibit_switch = 0;
+	bool wa_applied = false;
+
+	if (!IS_SRIOV_VF(gt_to_xe(gt)) && XE_GT_WA(gt, 16023105232)) {
+		/* xe_gt_clock_init() warns and zeroes timestamp_base on unknown crystal clock. */
+		if (!idledly_units_ps)
+			return;
 
-	if (!IS_SRIOV_VF(gt_to_xe(hwe->gt)) && XE_GT_WA(gt, 16023105232)) {
 		idledly = xe_mmio_read32(&gt->mmio, RING_IDLEDLY(hwe->mmio_base));
 		maxcnt = xe_mmio_read32(&gt->mmio, RING_PWRCTX_MAXCNT(hwe->mmio_base));
 
 		inhibit_switch = idledly & INHIBIT_SWITCH_UNTIL_PREEMPTED;
 		idledly = REG_FIELD_GET(IDLE_DELAY, idledly);
-		idledly = DIV_ROUND_CLOSEST(idledly * idledly_units_ps, 1000);
+		idledly = DIV_ROUND_CLOSEST_ULL((u64)idledly * idledly_units_ps, 1000);
 		maxcnt = REG_FIELD_GET(IDLE_WAIT_TIME, maxcnt);
 		maxcnt *= maxcnt_units_ns;
 
-		if (xe_gt_WARN_ON(gt, idledly >= maxcnt || inhibit_switch)) {
-			idledly = DIV_ROUND_CLOSEST(((maxcnt - 1) * 1000),
-						    idledly_units_ps);
-			xe_mmio_write32(&gt->mmio, RING_IDLEDLY(hwe->mmio_base), idledly);
+		/* Clear the inhibit switch without disturbing a valid delay. */
+		if (inhibit_switch)
+			wa_applied = true;
+
+		if (xe_gt_WARN_ON(gt, idledly >= maxcnt)) {
+			/* Floor below maxcnt; write 0 to still clear the inhibit bit. */
+			idledly = maxcnt ?
+				DIV_ROUND_DOWN_ULL((u64)(maxcnt - 1) * 1000,
+						   idledly_units_ps) : 0;
+			wa_applied = true;
 		}
+
+		if (wa_applied)
+			xe_mmio_write32(&gt->mmio, RING_IDLEDLY(hwe->mmio_base),
+					REG_FIELD_PREP(IDLE_DELAY, idledly));
 	}
 }
 
author maintainer
#304 lore raw

PATCH 304/457 drm/xe: Limit sg segment size to PAGE_SIZE on Xen PV

In reply to Greg Kroah-Hartman

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

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

From: Szymon Acedański <[email protected]>

commit 141008dec73521ccf64878517460cec8b3297251 upstream.

Fix display corruption on Xen PV dom0, where DMA buffers are not guaranteed machine-contiguous, in which case bounce buffering kicks in, breaking xe's memory coherency assumptions.

Apply the same workaround i915 carries in i915_sg_segment_size() since commit 78a07fe777c4 ("drm/i915: stop abusing swiotlb_max_segment").

---
 drivers/gpu/drm/xe/xe_bo.h |   19 +++++++++++++++++++
 1 file changed, 19 insertions(+)
drivers/gpu/drm/xe/xe_bo.h +19 -0
@@ -9,6 +9,8 @@
 #include <drm/drm_prime.h>
 #include <drm/ttm/ttm_tt.h>
 
+#include <xen/xen.h>
+
 #include "xe_bo_types.h"
 #include "xe_ggtt.h"
 #include "xe_macros.h"
@@ -574,6 +576,23 @@ static inline unsigned int xe_sg_segment
 	struct scatterlist __maybe_unused sg;
 	size_t max = BIT_ULL(sizeof(sg.length) * 8) - 1;
 
+	/*
+	 * For Xen PV guests pages aren't contiguous in DMA (machine) address
+	 * space.  The DMA API takes care of that both in dma_alloc_* (by
+	 * calling into the hypervisor to make the pages contiguous) and in
+	 * dma_map_* (by bounce buffering).  But xe (like i915, see commit
+	 * 78a07fe777c4) ignores the coherency aspects of the DMA API and thus
+	 * can't cope with bounce buffering actually happening, so add a hack
+	 * here to force small allocations and mappings when running in PV
+	 * mode on Xen.
+	 *
+	 * Note this will still break if bounce buffering is required for other
+	 * reasons, like confidential computing hypervisors or PCIe root ports
+	 * with addressing limitations.
+	 */
+	if (xen_pv_domain())
+		return PAGE_SIZE;
+
 	max = min_t(size_t, max, dma_max_mapping_size(dev));
 
 	/*
author maintainer
#305 lore raw

PATCH 305/457 drm/xe: Keep walking on SVM eviction failure

In reply to Greg Kroah-Hartman

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

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

From: Matthew Brost <[email protected]>

commit be1df8badae513e01d9575398438716cfae18655 upstream.

The desired behavior for SVM eviction failures, which can occur due to various uncontrollable races, is for TTM to continue walking the LRU list and look for another eviction candidate. This is expressed by returning -ENOSPC from the ->move() callback.

Adjust the SVM eviction failure path because of races in ->move() to return -ENOSPC so that TTM continues searching for another buffer to evict.

Fixes 3ca608dc7561 ("drm/xe: Basic SVM BO eviction")
(cherry picked from commit 36a86c23588b8f57c9d20feb4cf5a2ab27e3baba)
---
 drivers/gpu/drm/xe/xe_bo.c |    7 +++++++
 1 file changed, 7 insertions(+)
drivers/gpu/drm/xe/xe_bo.c +7 -0
@@ -1025,6 +1025,13 @@ static int xe_bo_move(struct ttm_buffer_
 		} else {
 			drm_dbg(&xe->drm, "Evict system allocator BO failed=%pe\n",
 				ERR_PTR(ret));
+			/*
+			 * The semantic we want upon SVM eviction failure
+			 * because of racing access is keep walking for
+			 * eviction, which is -ENOSPC.
+			 */
+			if (ret == -EBUSY)
+				ret = -ENOSPC;
 		}
 
 		goto out;
author maintainer
#306 lore raw

PATCH 306/457 drm/virtio: fix memory leak of fence event on execbuffer failure

In reply to Greg Kroah-Hartman

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

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

From: Peiyang He <[email protected]>

commit b74aad23d99b279bb34d135795f39a6d8ecdc075 upstream.

virtio_gpu_execbuffer_ioctl() reserves a DRM event with drm_event_reserve_init() when VIRTGPU_EXECBUF_RING_IDX selects a ring that userspace has enabled polling for. virtio_gpu_init_submit() does this before the BO handles, the command buffer, the syncobj arrays and the in-fence are processed, so every later error path runs with the event already pending, including plain argument validation failures such as an invalid bo_handle or an in-syncobj that carries no fence.

On those paths, virtio_gpu_cleanup_submit() drops the out-fence without cancelling the event. The fence is freed without ever having been emitted, taking the only driver-side pointer to the event with it. Closing the DRM file does not help. drm_events_release() unlinks pending events but deliberately leaves the freeing to the driver's later drm_send_event(), which never runs for an orphaned event, so the allocation is leaked permanently.

Found when fuzzing the virtio driver with Syzkaller:

	BUG: memory leak
	unreferenced object 0xffff88802c176e80 (size 96):
	comm "syz.1.367", pid 10561, jiffies 4294960122
	hex dump (first 32 bytes):
		00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
		c8 6e 17 2c 80 88 ff ff 00 00 00 00 00 00 00 00  .n.,............
	backtrace (crc e1973c6b):
		kmemleak_alloc_recursive include/linux/kmemleak.h:44 [inline]
		slab_post_alloc_hook mm/slub.c:4597 [inline]
		slab_alloc_node mm/slub.c:4917 [inline]
		__kmalloc_cache_noprof+0x49d/0x6f0 mm/slub.c:5485
		_kmalloc_noprof include/linux/slab.h:988 [inline]
		_kzalloc_noprof include/linux/slab.h:1309 [inline]
		virtio_gpu_fence_event_create drivers/gpu/drm/virtio/virtgpu_submit.c:282 [inline]
		virtio_gpu_init_submit drivers/gpu/drm/virtio/virtgpu_submit.c:398 [inline]
		virtio_gpu_execbuffer_ioctl+0xbbf/0x1aa0 drivers/gpu/drm/virtio/virtgpu_submit.c:505
		drm_ioctl_kernel+0x1f4/0x3e0 drivers/gpu/drm/drm_ioctl.c:817
		drm_ioctl+0x5f4/0xc70 drivers/gpu/drm/drm_ioctl.c:914
		vfs_ioctl fs/ioctl.c:51 [inline]
		__do_sys_ioctl fs/ioctl.c:597 [inline]
		__se_sys_ioctl fs/ioctl.c:583 [inline]
		__x64_sys_ioctl+0x18e/0x210 fs/ioctl.c:583
		do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
		do_syscall_64+0x116/0x800 arch/x86/entry/syscall_64.c:94
		entry_SYSCALL_64_after_hwframe+0x77/0x7f

Fix by cancelling and freeing the DRM event on the execbuffer error path before dropping the fence. Clear the fence's event pointer after cancellation so it does not retain a dangling pointer.

Fixes cd7f5ca33585 ("drm/virtio: implement context init: add virtio_gpu_fence_event")
Assisted-by Codex:gpt-5.6-luna
---
 drivers/gpu/drm/virtio/virtgpu_submit.c |    4 ++++
 1 file changed, 4 insertions(+)
drivers/gpu/drm/virtio/virtgpu_submit.c +4 -0
@@ -538,6 +538,10 @@ int virtio_gpu_execbuffer_ioctl(struct d
 	virtio_gpu_process_post_deps(&submit);
 	virtio_gpu_complete_submit(&submit);
 cleanup:
+	if (ret && submit.out_fence && submit.out_fence->e) {
+		drm_event_cancel_free(dev, &submit.out_fence->e->base);
+		submit.out_fence->e = NULL;
+	}
 	virtio_gpu_cleanup_submit(&submit);
 
 	return ret;
author maintainer
#307 lore raw

PATCH 307/457 drm/virtio: fix NULL pointer dereference on fence allocation failure

In reply to Greg Kroah-Hartman

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

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

From: Peiyang He <[email protected]>

commit 846b3c64fe3e77d9db20a7e3e62dbbb637c773e1 upstream.

virtio_gpu_fence_alloc() can fail due to memory pressure and return NULL, but its caller like virtio_gpu_init_submit() never checks it. Later, virtio_gpu_init_submit() passes the NULL fence to virtio_gpu_fence_event_create(), which unconditionally dereferences it.

Found when fuzzing the virtio driver with Syzkaller:

	Oops: general protection fault, probably for non-canonical address 0xdffffc0000000012: 0000 [#1] SMP KASAN NOPTI
	KASAN: null-ptr-deref in range [0x0000000000000090-0x0000000000000097]
	CPU: 1 UID: 0 PID: 9991 Comm: syz.0.121 Not tainted 7.2.0 #4 PREEMPT(full)
	Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS rel-1.17.0-0-gb52ca86e094d-prebuilt.qemu.org 04/01/2014
	RIP: 0010:virtio_gpu_fence_event_create drivers/gpu/drm/virtio/virtgpu_submit.c:295 [inline]
	RIP: 0010:virtio_gpu_init_submit drivers/gpu/drm/virtio/virtgpu_submit.c:398 [inline]
	RIP: 0010:virtio_gpu_execbuffer_ioctl+0xc78/0x1aa0 drivers/gpu/drm/virtio/virtgpu_submit.c:505
	Code: 85 ed 0f 85 21 09 00 00 e8 05 5a c9 fb 48 8b 44 24 10 48 8d b8 90 00 00 00 48 b8 00 00 00 00 00 fc ff df 48 89 fa 48 c1 ea 03 <80> 3c 02 00 0f 85 9a 0d 00 00 48 8b 44 24 10 4c 89 b0 90 00 00 00
	RSP: 0018:ffffc900039dfad0 EFLAGS: 00010216
	RAX: dffffc0000000000 RBX: ffffc900039dfdd8 RCX: ffffffff85f6fd3d
	RDX: 0000000000000012 RSI: ffffffff85f6fd4b RDI: 0000000000000090
	RBP: 0000000000000000 R0virtio_gpu_virgl_process_cmd: ctrl 0x102, error 0x1203
	R10: 0000000000000000 R11: 0000000000000000 R12: ffff8880132c4000
	R13: 0000000000000000 R14: ffff888073b6c700 R15: 000000000000003b
	FS:  00007fab480b96c0(0000) GS:ffff8880eb6e9000(0000) knlGS:0000000000000000
	CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
	CR2: 00007effbf5e55a8 CR3: 0000000048d19000 CR4: 0000000000350ef0
	Call Trace:
	<TASK>
	drm_ioctl_kernel+0x1f4/0x3e0 drivers/gpu/drm/drm_ioctl.c:817
	drm_ioctl+0x5f4/0xc70 drivers/gpu/drm/drm_ioctl.c:914
	vfs_ioctl fs/ioctl.c:51 [inline]
	__do_sys_ioctl fs/ioctl.c:597 [inline]
	__se_sys_ioctl fs/ioctl.c:583 [inline]
	__x64_sys_ioctl+0x18e/0x210 fs/ioctl.c:583
	do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
	do_syscall_64+0x116/0x800 arch/x86/entry/syscall_64.c:94
	entry_SYSCALL_64_after_hwframe+0x77/0x7f
	RIP: 0033:0x7fab471a82bd
	Code: ff c3 66 2e 0f 1f 84 00 00 00 00 00 90 f3 0f 1e fa 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 b0 ff ff ff f7 d8 64 89 01 48
	RSP: 002b:00007fab480b9018 EFLAGS: 00000246 ORIG_RAX: 0000000000000010
	RAX: ffffffffffffffda RBX: 00007fab47435fa0 RCX: 00007fab471a82bd
	RDX: 00002000000000c0 RSI: 00000000c0406442 RDI: 0000000000000003
	RBP: 00007fab480b9080 R08: 0000000000000000 R09: 0000000000000000
	R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000001
	R13: 00007fab47436038 R14: 00007fab47435fa0 R15: 00007ffe85ab0740
	</TASK>
	Modules linked in:
	---[ end trace 0000000000000000 ]---
	RIP: 0010:virtio_gpu_fence_event_create drivers/gpu/drm/virtio/virtgpu_submit.c:295 [inline]
	RIP: 0010:virtio_gpu_init_submit drivers/gpu/drm/virtio/virtgpu_submit.c:398 [inline]
	RIP: 0010:virtio_gpu_execbuffer_ioctl+0xc78/0x1aa0 drivers/gpu/drm/virtio/virtgpu_submit.c:505
	Code: 85 ed 0f 85 21 09 00 00 e8 05 5a c9 fb 48 8b 44 24 10 48 8d b8 90 00 00 00 48 b8 00 00 00 00 00 fc ff df 48 89 fa 48 c1 ea 03 <80> 3c 02 00 0f 85 9a 0d 00 00 48 8b 44 24 10 4c 89 b0 90 00 00 00
	RSP: 0018:ffffc900039dfad0 EFLAGS: 00010216
	RAX: dffffc0000000000 RBX: ffffc900039dfdd8 RCX: ffffffff85f6fd3d
	RDX: 0000000000000012 RSI: ffffffff85f6fd4b RDI: 0000000000000090
	RBP: 0000000000000000 R08: 0000000000000005 R09: 0000000000000000
	R10: 0000000000000000 R11: 0000000000000000 R12: ffff8880132c4000
	R13: 0000000000000000 R14: ffff888073b6c700 R15: 000000000000003b
	FS:  00007fab480b96c0(0000) GS:ffff888098ae9000(0000) knlGS:0000000000000000
	CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
	CR2: 00007f24c3759000 CR3: 0000000048d19000 CR4: 0000000000350ef0
	----------------
	Code disassembly (best guess):
	0:	85 ed                	test   %ebp,%ebp
	2:	0f 85 21 09 00 00    	jne    0x929
	8:	e8 05 5a c9 fb       	call   0xfbc95a12
	d:	48 8b 44 24 10       	mov    0x10(%rsp),%rax
	12:	48 8d b8 90 00 00 00 	lea    0x90(%rax),%rdi
	19:	48 b8 00 00 00 00 00 	movabs $0xdffffc0000000000,%rax
	20:	fc ff df
	23:	48 89 fa             	mov    %rdi,%rdx
	26:	48 c1 ea 03          	shr    $0x3,%rdx
	* 2a:	80 3c 02 00          	cmpb   $0x0,(%rdx,%rax,1) <-- trapping instruction
	2e:	0f 85 9a 0d 00 00    	jne    0xdce
	34:	48 8b 44 24 10       	mov    0x10(%rsp),%rax
	39:	4c 89 b0 90 00 00 00 	mov    %r14,0x90(%rax)

Fix by checking virtio_gpu_fence_alloc() in virtio_gpu_init_submit() and returning -ENOMEM before any later code can dereference the NULL fence.

---
 drivers/gpu/drm/virtio/virtgpu_submit.c |    7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)
drivers/gpu/drm/virtio/virtgpu_submit.c +5 -2
@@ -389,10 +389,13 @@ static int virtio_gpu_init_submit(struct
 	if ((exbuf->flags & VIRTGPU_EXECBUF_FENCE_FD_OUT) ||
 	    exbuf->num_out_syncobjs ||
 	    exbuf->num_bo_handles ||
-	    drm_fence_event)
+	    drm_fence_event) {
 		out_fence = virtio_gpu_fence_alloc(vgdev, fence_ctx, ring_idx);
-	else
+		if (!out_fence)
+			return -ENOMEM;
+	} else {
 		out_fence = NULL;
+	}
 
 	if (drm_fence_event) {
 		err = virtio_gpu_fence_event_create(dev, file, out_fence, ring_idx);
author maintainer
#308 lore raw

PATCH 308/457 gpio: tps65219: Fix GPIO input value reads

In reply to Greg Kroah-Hartman

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

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

From: Karl Mehltretter <[email protected]>

commit 4cbe530c0233c7413aaaeb029a4f32dd6aadacbb upstream.

TPS65219_MFP_GPIO_STATUS_MASK is already BIT(4). Passing it to BIT() again tests bit 16, which cannot be set in the 8-bit MFP_CTRL register, so GPIO0 is always reported low when configured as an input.

Test the register value with the mask directly.

Fixes 57e30e00bd5b ("gpio: tps65219: add GPIO support for TPS65219 PMIC")
Assisted-by LLM
---
 drivers/gpio/gpio-tps65219.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/gpio/gpio-tps65219.c +1 -1
@@ -79,7 +79,7 @@ static int tps65219_gpio_get(struct gpio
 	if (ret)
 		return ret;
 
-	ret = !!(val & BIT(TPS65219_MFP_GPIO_STATUS_MASK));
+	ret = !!(val & TPS65219_MFP_GPIO_STATUS_MASK);
 	dev_warn(dev, "GPIO%d = %d, MULTI_DEVICE_ENABLE, not a standard GPIO\n", offset, ret);
 
 	/*
author maintainer
#309 lore raw

PATCH 309/457 gpio: tps65219: Use the variant-specific direction callback

In reply to Greg Kroah-Hartman

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

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

From: Karl Mehltretter <[email protected]>

commit 93cf8cedeaaa05714f709b539cfb976e0b80c830 upstream.

The TPS65214 template installs its own get_direction callback because its direction bit is in GENERAL_CONFIG. The shared get and direction callbacks nevertheless call tps65219_gpio_get_direction() directly and interpret the unrelated TPS65219 MFP bit.

On TPS65214 this can reject reads from an input and skip the change from input to output. Call the callback selected by the gpio_chip template instead.

Fixes 1b6ab07c0c80 ("gpio: tps65219: Add support for TI TPS65214 PMIC")
Assisted-by LLM
---
 drivers/gpio/gpio-tps65219.c |    6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)
drivers/gpio/gpio-tps65219.c +3 -3
@@ -87,7 +87,7 @@ static int tps65219_gpio_get(struct gpio
 	 * status bit.
 	 */
 
-	if (tps65219_gpio_get_direction(gc, offset) == GPIO_LINE_DIRECTION_OUT)
+	if (gc->get_direction(gc, offset) == GPIO_LINE_DIRECTION_OUT)
 		return -ENOTSUPP;
 
 	return ret;
@@ -176,7 +176,7 @@ static int tps65219_gpio_direction_input
 		return -ENOTSUPP;
 	}
 
-	if (tps65219_gpio_get_direction(gc, offset) == GPIO_LINE_DIRECTION_IN)
+	if (gc->get_direction(gc, offset) == GPIO_LINE_DIRECTION_IN)
 		return 0;
 
 	return gpio->change_dir(gc, offset, GPIO_LINE_DIRECTION_IN);
@@ -190,7 +190,7 @@ static int tps65219_gpio_direction_outpu
 	if (offset != TPS6521X_GPIO0_IDX)
 		return 0;
 
-	if (tps65219_gpio_get_direction(gc, offset) == GPIO_LINE_DIRECTION_OUT)
+	if (gc->get_direction(gc, offset) == GPIO_LINE_DIRECTION_OUT)
 		return 0;
 
 	return gpio->change_dir(gc, offset, GPIO_LINE_DIRECTION_OUT);
author maintainer
#310 lore raw

PATCH 310/457 gpio: tps65219: Fix TPS65214 GPIO direction programming

In reply to Greg Kroah-Hartman

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

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

From: Karl Mehltretter <[email protected]>

commit 270437f3fe62516f16482742a7762a075e7a9457 upstream.

GPIO_LINE_DIRECTION_OUT and GPIO_LINE_DIRECTION_IN have the values 0 and 1, respectively, while the TPS65214 GPIO_CONFIG field is BIT(1). regmap_update_bits() masks the supplied value, so passing either direction value clears the field and selects input mode.

Translate the GPIO direction to the register encoding used by tps65214_gpio_get_direction(), setting GPIO_CONFIG for output and clearing it for input.

Fixes 1b6ab07c0c80 ("gpio: tps65219: Add support for TI TPS65214 PMIC")
Assisted-by LLM
---
 drivers/gpio/gpio-tps65219.c |    4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/gpio/gpio-tps65219.c +3 -1
@@ -158,8 +158,10 @@ static int tps65214_gpio_change_directio
 	if (ret)
 		dev_err(dev, "GPIO%d configured as VSEL, not GPIO\n", offset);
 
+	val = direction == GPIO_LINE_DIRECTION_OUT ?
+		TPS65214_GPIO0_DIR_MASK : 0;
 	ret = regmap_update_bits(gpio->tps->regmap, TPS65219_REG_GENERAL_CONFIG,
-				 TPS65214_GPIO0_DIR_MASK, direction);
+				 TPS65214_GPIO0_DIR_MASK, val);
 	if (ret)
 		dev_err(dev, "Fail to change direction to %u for GPIO%d.\n", direction, offset);
 
author maintainer
#311 lore raw

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

In reply to Greg Kroah-Hartman

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

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

From: Jinjiang Tu <[email protected]>

commit b6ac0b3f6013c168f22cad97e79967accacb08e1 upstream.

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
@@ -209,7 +209,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
@@ -1995,6 +1995,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
@@ -2009,6 +2016,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
#312 lore raw

PATCH 312/457 mm/hugetlb: do not dissolve gigantic pages without runtime support

In reply to Greg Kroah-Hartman

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

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

From: Longlong Xia <[email protected]>

commit a363c62a653cc8b3e21da9545fa4e028ef50f9c3 upstream.

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
@@ -1977,6 +1977,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
#313 lore raw

PATCH 313/457 mm/hugetlb: preserve mremap address delta when skipping page tables

In reply to Greg Kroah-Hartman

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

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

From: Jaewook You <[email protected]>

commit 9bdad082d44bdcf93716973dcba6be77e8a06e7b upstream.

move_hugetlb_page_tables() optimizes mremap() by advancing to the last entry in the page table when the source page table does not exist, either initially or after unsharing a PMD table. The common loop increment then steps to the first entry in the next page table.

However, the code advances both the source and destination addresses to the last entries in their respective page tables, which is wrong. The destination address must be advanced only by the same amount as the source address.

If the source and destination offsets within their page tables differ, the destination address can be advanced too far, causing follow-up issues. Fix this by advancing the destination address by the source advance distance.

With a reproducer, we were able to trigger a kernel panic on x86-64. With this fix in place, we can no longer reproduce the issue.

Fixes e95a9851787b ("hugetlb: skip to end of PT page mapping when pte not present")
Fixes 4ddb4d91b82f ("hugetlb: do not update address in huge_pmd_unshare")
Cc Johan Hovold <[email protected]>
Cc Muchun Song <[email protected]>
Cc Oscar Salvador <[email protected]>
Assisted-by LLM
---
 mm/hugetlb.c |   11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)
mm/hugetlb.c +7 -4
@@ -5143,18 +5143,21 @@ int move_hugetlb_page_tables(struct vm_a
 	hugetlb_vma_lock_write(vma);
 	i_mmap_lock_write(mapping);
 	for (; old_addr < old_end; old_addr += sz, new_addr += sz) {
+		const unsigned long offset_to_last_entry =
+			(old_addr | last_addr_mask) - old_addr;
+
 		src_pte = hugetlb_walk(vma, old_addr, sz);
 		if (!src_pte) {
-			old_addr |= last_addr_mask;
-			new_addr |= last_addr_mask;
+			old_addr += offset_to_last_entry;
+			new_addr += offset_to_last_entry;
 			continue;
 		}
 		if (huge_pte_none(huge_ptep_get(mm, old_addr, src_pte)))
 			continue;
 
 		if (huge_pmd_unshare(&tlb, vma, old_addr, src_pte)) {
-			old_addr |= last_addr_mask;
-			new_addr |= last_addr_mask;
+			old_addr += offset_to_last_entry;
+			new_addr += offset_to_last_entry;
 			continue;
 		}
 
author maintainer
#314 lore raw

PATCH 314/457 mm/damon/vaddr: avoid hw-driven pte updates during damon_hugetlb_mkold()

In reply to Greg Kroah-Hartman

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

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

From: SJ Park <[email protected]>

commit 39c0ceedd54557bdc1542de08d22b2ed33e534e4 upstream.

damon_hugetlb_mkold() reads the page table entry into a local variable, unsets the accessed bit in the variable, and updates the page table entry with the updated variable value. If hardware updates the same page table entry in parallel, the hw updates could be lost. For example, hardware-updated dirty bits might be lost.

Avoid the parallel updates by clearing the page table entry when reading it together, using huge_ptep_get_and_clear(). If a parallel write to the memory is made after the clearing, the hw will see the page table entry is cleared, trigger page fault and wait until it is handled. The page fault handling will wait for damon_hugetlb_mkold() due to the page table lock.

Because hugetlbfs is an in-memory file system and hugetlb pages cannot be reclaimed, no critical issue is expected to my best knowledge. But definitely this is a nasty bug that should be fixed sooner rather than later.

The issue was discovered [1] by Sashiko.

---
 mm/damon/vaddr.c |   21 ++++++++++++++-------
 1 file changed, 14 insertions(+), 7 deletions(-)
mm/damon/vaddr.c +14 -7
@@ -292,22 +292,29 @@ out:
 }
 
 #ifdef CONFIG_HUGETLB_PAGE
+static bool damon_hugetlb_ptep_mkold(pte_t *pte, struct mm_struct *mm,
+		struct vm_area_struct *vma, unsigned long addr, pte_t *entry)
+{
+	unsigned long psize = huge_page_size(hstate_vma(vma));
+
+	if (!pte_young(*entry))
+		return false;
+	*entry = huge_ptep_get_and_clear(mm, addr, pte, psize);
+	*entry = pte_mkold(*entry);
+	set_huge_pte_at(mm, addr, pte, *entry, psize);
+	return true;
+}
+
 static void damon_hugetlb_mkold(pte_t *pte, struct mm_struct *mm,
 				struct vm_area_struct *vma, unsigned long addr)
 {
 	bool referenced = false;
 	pte_t entry = huge_ptep_get(mm, addr, pte);
 	struct folio *folio = pfn_folio(pte_pfn(entry));
-	unsigned long psize = huge_page_size(hstate_vma(vma));
 
 	folio_get(folio);
 
-	if (pte_young(entry)) {
-		referenced = true;
-		entry = pte_mkold(entry);
-		set_huge_pte_at(mm, addr, pte, entry, psize);
-	}
-
+	referenced = damon_hugetlb_ptep_mkold(pte, mm, vma, addr, &entry);
 	if (mmu_notifier_clear_young(mm, addr,
 				     addr + huge_page_size(hstate_vma(vma))))
 		referenced = true;
author maintainer
#315 lore raw

PATCH 315/457 mm/damon/ops-common: use a page-aligned address in damon_ptep_mkold()

In reply to Greg Kroah-Hartman

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

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

From: Nathan Gao <[email protected]>

commit f166586f74dd5d9cbadaabf86ef81c8ddf6cafa7 upstream.

__damon_va_prepare_access_check() picks a random byte address within the region and stores it in r->sampling_addr. damon_va_mkold() passes it into a page table walk, which hands it to damon_ptep_mkold() as the address of the page to sample:

damon_va_mkold(mm, r->sampling_addr)
damon_va_walk_page_range(mm, addr, addr + 1)
damon_mkold_pmd_entry()
damon_ptep_mkold(pte, vma, addr)
ptep_test_and_clear_young(vma, addr, pte) mmu_notifier_clear_young(mm, addr, addr + PAGE_SIZE)

For arm64, before commit 6f0e1142173a ("arm64: mm: support batch clearing of the young flag for large folios"), the contpte helper walked exactly CONT_PTES entries from the aligned-down page table pointer and used @addr only to pass down to each entry, so an unaligned value was harmless:

        ptep = contpte_align_down(ptep);
        addr = ALIGN_DOWN(addr, CONT_PTE_SIZE);
        for (i = 0; i < CONT_PTES; i++, ptep++, addr += PAGE_SIZE)

Now the range to walk is derived from @addr instead: end = addr + nr * PAGE_SIZE, rounded up to CONT_PTE_SIZE. For a sample in the last page of a contpte block, the sub-page offset puts end just past the block boundary, so the round-up lands a whole block further and the walk clears PTE_AF in CONT_PTES entries beyond the sampled block. For the last block in a page table page, those entries are past the end of that page, so the walk writes into the page that follows.

Triggered by the full 7.1/7.2 kernel selftest suite on arm64 (EC2 c/m6g.4xlarge). The kernel sometimes crashes at or shortly after the DAMON test.

What the overrun does depends on the page that happens to follow the page table, so there is no single signature. If that page is read-only, the write faults in the sampling path itself:

  Unable to handle kernel write to read-only memory at virtual address ffff0003c5d2d000
    FSC = 0x0f: level 3 permission fault
    CM = 0, WnR = 1, TnD = 0, TagAccess = 0
  CPU: 10 UID: 0 PID: 3487 Comm: kdamond.2
  pc : contpte_test_and_clear_young_ptes+0x70/0xc0
  lr : damon_ptep_mkold+0x1e8/0x1f8
  Call trace:
   contpte_test_and_clear_young_ptes+0x70/0xc0 (P)
   damon_mkold_pmd_entry+0x150/0x170
   walk_pmd_range+0x110/0x2b0
   walk_pud_range+0x10c/0x208
   walk_pgd_range+0x134/0x258
   __walk_page_range+0x98/0x1b0
   walk_page_range_vma_unsafe+0x90/0x148
   walk_page_range_vma+0x28/0x40
   damon_va_walk_page_range+0x114/0x2b8
   damon_va_prepare_access_checks+0xec/0x1a8
   kdamond_fn+0x534/0x770
   kthread+0x128/0x138
   ret_from_fork+0x10/0x20

Otherwise the page is writable, the PTE_AF clearing succeeds silently and the damage only surfaces later, in whatever happened to own the page, so the backtrace is unrelated to DAMON and differs between runs.

Pass a page-aligned address to the ptep_test_and_clear_young() call in damon_ptep_mkold(), which is the only place DAMON can reach contpte_test_and_clear_young_ptes() from. Nothing else sees the aligned address, and r->sampling_addr itself is left as is, so the sampling and region bookkeeping semantics are unchanged.

Fixes 6f0e1142173a ("arm64: mm: support batch clearing of the young flag for large folios")
Cc Baolin Wang <[email protected]>
Cc David Hildenbrand (Arm) <[email protected]>
Cc Ryan Roberts <[email protected]>
---
 mm/damon/ops-common.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)
mm/damon/ops-common.c +6 -1
index fbda70d8ea4d..8fc61d06d358 100644
@@ -61,7 +61,12 @@ void damon_ptep_mkold(pte_t *pte, struct vm_area_struct *vma, unsigned long addr
 	 * device aspects.
 	 */
 	if (likely(pte_present(pteval)))
-		young |= ptep_test_and_clear_young(vma, addr, pte);
+		/*
+		 * Arch implementation of ptep_test_and_clear_young() may
+		 * require aligned @addr
+		 */
+		young |= ptep_test_and_clear_young(vma, PAGE_ALIGN_DOWN(addr),
+				pte);
 	young |= mmu_notifier_clear_young(vma->vm_mm, addr, addr + PAGE_SIZE);
 	if (young)
 		folio_set_young(folio);
Signature · 1 line
2.55.0
author maintainer
#316 lore raw

PATCH 316/457 mm/damon/core: fix unconditionally skip last region

In reply to Greg Kroah-Hartman

7.2-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 |   25 ++++++++++++++-----------
 1 file changed, 14 insertions(+), 11 deletions(-)
mm/damon/core.c +14 -11
@@ -2145,36 +2145,39 @@ static bool damos_skip_charged_region(st
 {
 	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_region_sz);
 			if (!sz_to_skip) {
-				if (damon_sz_region(r) <= min_region_sz)
-					return true;
+				if (damon_sz_region(r) <= min_region_sz) {
+					skip = true;
+					goto out;
+				}
 				sz_to_skip = min_region_sz;
 			}
 			damon_split_region_at(t, r, sz_to_skip);
-			return true;
+			skip = true;
 		}
+	}
+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
#317 lore raw

PATCH 317/457 mm/damon/core: allow esz to be set to zero

In reply to Greg Kroah-Hartman

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

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

From: Liew Rui Yan <[email protected]>

commit 90179da203ba8b708c84a12a07cd44be0f346334 upstream.

When the temporal quota goal tuner determines that the goal has been achieved (score >= 10000), it sets esz_bp to zero so that the esz becomes zero. However, damos_set_effective_quota() clamps the esz to min_region_sz when quota->ms is set.

This is a minor issue, the main problem is that it doesn't match the description in the documentation, which state that if the goal has already been [over-]achieved, the quota will be set to zero.

Fix this by set quota (esz) as minimum as possible.

Fixes 8bbde987c2b8 ("mm/damon/core: disallow time-quota setting zero esz")
Cc <[email protected]> # v7.1.x
---
 mm/damon/core.c |    5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)
mm/damon/core.c +3 -2
@@ -2896,6 +2896,7 @@ static void damos_set_effective_quota(st
 	struct damos_quota *quota = &s->quota;
 	unsigned long throughput;
 	unsigned long esz = ULONG_MAX;
+	unsigned long esz_time;
 
 	if (!quota->ms && list_empty(&quota->goals)) {
 		quota->esz = quota->sz;
@@ -2916,8 +2917,8 @@ static void damos_set_effective_quota(st
 					1000000, quota->total_charged_ns);
 		else
 			throughput = PAGE_SIZE * 1024;
-		esz = min(throughput * quota->ms, esz);
-		esz = max(ctx->min_region_sz, esz);
+		esz_time = max(throughput * quota->ms, ctx->min_region_sz);
+		esz = min(esz_time, esz);
 	}
 
 	if (quota->sz && quota->sz < esz)
author maintainer
#318 lore raw

PATCH 318/457 mm/damon/core: reset invalid quota->charge_target_from

In reply to Greg Kroah-Hartman

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

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

From: SJ Park <[email protected]>

commit eb64948249781bda35de04feab5a0acc36aa9051 upstream.

DAMOS can suddenly stop working if a target process that the quota is just fully charged on is terminated. Fix by catching and processing the corner case.

When DAMOS quota is fully charged, the target and the region to continue applying the action in the next round is saved in damos_quota->charge_{target,addr}_from. In the next round, DAMOS iterates targets and regions from the beginning. It skips applying the action to the regions until it visits and skips the saved target/region.

Virtual address space targets become invalid if the process is terminated. Trying to apply the scheme to invalid target is just a waste of time. Hence commit 6e4930e33329 ("mm/damon/core: fix wasteful CPU calls by skipping non-existent targets") made the logic to skip invalid targets. However, it does skip before the charged target/region skipping/updating.

Let's suppose the user runs DAMOS for multiple virtual address spaces with a quota. The quota exceeded in the middle of a virtual address space. And the process of the address space is terminated. Then the charge_target_from points to the invalid target. The pointer update logic is skipped for the invalid target, so the charge_target_from is never updated. DAMOS action to every target/region is skipped. From the user's perspective, it would look like suddenly DAMOS has stopped working.

No critical leak or crash can happen. The user could reinstall the scheme. But this makes use of DAMOS under certain setups quite unreliable.

When the invalid target is found, further check the corner case and reset the pointer.

This issue was discovered [1] by Sashiko.

Fixes 6e4930e33329 ("mm/damon/core: fix wasteful CPU calls by skipping non-existent targets")
Cc Enze Li <[email protected]>
Cc <[email protected]> # 7.0.x
---
 mm/damon/core.c |    9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)
mm/damon/core.c +8 -1
@@ -3047,8 +3047,15 @@ static void kdamond_apply_schemes(struct
 	max_region_sz = damon_region_sz_limit(c);
 	mutex_lock(&c->walk_control_lock);
 	damon_for_each_target(t, c) {
-		if (c->ops.target_valid && c->ops.target_valid(t) == false)
+		if (c->ops.target_valid && c->ops.target_valid(t) == false) {
+			damon_for_each_scheme(s, c) {
+				if (s->quota.charge_target_from != t)
+					continue;
+				s->quota.charge_target_from = NULL;
+				s->quota.charge_addr_from = 0;
+			}
 			continue;
+		}
 		damos_apply_target(c, t, max_region_sz);
 	}
 
author maintainer
#319 lore raw

PATCH 319/457 PCI: Fix BAR resize for devices on a root bus

In reply to Greg Kroah-Hartman

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

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

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

commit d58384c22739848efe14b34e9586e4f1242f33c0 upstream.

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]
---
 drivers/pci/setup-bus.c |   25 ++++++++++++++++++-------
 1 file changed, 18 insertions(+), 7 deletions(-)
drivers/pci/setup-bus.c +18 -7
@@ -2380,6 +2380,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;
@@ -2397,6 +2398,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;
@@ -2415,13 +2418,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 (!resource_assigned(dev_res->res)) {
+				ret = -ENOSPC;
+				goto restore;
+			}
+		}
+	}
 
 out:
 	up_read(&pci_bus_sem);
author maintainer
#320 lore raw

PATCH 320/457 PCI: of_property: Omit bus properties without a subordinate bus

In reply to Greg Kroah-Hartman

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

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

From: Angel J <[email protected]>

commit 8805840aad73df7146778be243a196d48b4f6430 upstream.

A bridge (a device with a Type 1 header) may not have a secondary bus allocated (pdev->subordinate), e.g., if there are no available bus numbers or the bridge secondary/subordinate bus numbers are not writable.

The dynamic OF helpers of_pci_prop_bus_range() and of_pci_prop_intr_map() dereference pdev->subordinate without checking it. When CONFIG_PCI_DYNAMIC_OF_NODES is enabled, this can cause a NULL pointer dereference and early boot hang.

Generate 'bus-range' and 'interrupt-map' properties only when a subordinate bus exists. Keep the node and its remaining properties for bridges without one.

The problem was latent since 407d1a51921e ("PCI: Create device tree node for bridge"), but wasn't reachable until 1f340724419e ("PCI: of: Create device tree PCI host bridge node"), which appeared in v6.15. Before 1f340724419e, of_pci_make_dev_node() returned early because the parent OF node was missing.

Fixes 407d1a51921e ("PCI: Create device tree node for bridge")
[bhelgaas: move pdev->subordinate test to callees, commit log]
---
 drivers/pci/of_property.c |   11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)
drivers/pci/of_property.c +9 -2
@@ -95,9 +95,13 @@ static int of_pci_prop_bus_range(struct
 				 struct of_changeset *ocs,
 				 struct device_node *np)
 {
-	u32 bus_range[] = { pdev->subordinate->busn_res.start,
-			    pdev->subordinate->busn_res.end };
+	u32 bus_range[2];
 
+	if (!pdev->subordinate)
+		return 0;
+
+	bus_range[0] = pdev->subordinate->busn_res.start;
+	bus_range[1] = pdev->subordinate->busn_res.end;
 	return of_changeset_add_prop_u32_array(ocs, np, "bus-range", bus_range,
 					       ARRAY_SIZE(bus_range));
 }
@@ -220,6 +224,9 @@ static int of_pci_prop_intr_map(struct p
 	int ret;
 	u8 pin;
 
+	if (!pdev->subordinate)
+		return 0;
+
 	pnode = pci_device_to_OF_node(pdev->bus->self);
 	if (!pnode)
 		pnode = pci_bus_to_OF_node(pdev->bus);
author maintainer
#321 lore raw

PATCH 321/457 packet: use ubuf_info completion for TX_RING packets

In reply to Greg Kroah-Hartman

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

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

From: Willem de Bruijn <[email protected]>

commit 9518405613863d0bf0700927a21367f5942cf058 upstream.

tpacket_snd sends skbs with frags pointing into its ring slots. Slots are released when skb->destructor is called.

A call to skb_orphan calls skb->destructor before the skb is freed. This can cause the slot to be reused while still linked into the skb.

Switch to standard zerocopy completion (ubuf_info) so the slot is only released once all references to the payload are freed or copied. Restore skb->destructor to standard sock_wfree.

The ubuf_info completion callback can be called with a NULL skb, but only from net_zcopy_put and related API, used by zerocopy implementations that hold their own reference on the uarg, such as MSG_ZEROCOPY. This uarg is only ever completed from skb_zcopy_clear, so skb is always set.

To prevent userspace from aliasing in-flight state on shared ring slots, allocate tpacket_uarg per packet, rather than per slot. This adds a small allocation to the transmit path. Use standard kmalloc to allow backporting to stable kernels.

The uarg holds an sk_wmem_alloc reference, rather than an sk_refcnt reference. packet_free_tx_ring waits on sk_wmem_alloc before freeing the ring pages. Always allocate vec->deferred for tx_ring so page-backed rings also wait on sk_wmem_alloc when skb_copy_ubufs drops page refs before calling tpacket_ubuf_complete.

Drop the tx_ring.pg_vec test that tpacket_destruct_skb performed before accessing the slot. The sk_wmem_alloc reference now guarantees that the slot is valid. The test is also not sufficient by itself, as it reads pg_vec without pg_vec_lock, so it can race with packet_set_ring.

As a result a slot is released when its payload is copied, which can be before transmission (e.g., in skb_orphan_frags_rx). If copied before skb_tx_timestamp() is called, no slot timestamp is recorded, similar to when skb_orphan() was called early in the datapath before this patch.

Revert the now unused previous skb_zcopy_.._nouarg infra.

Depends on commit 992cc9f94ca9 ("net/packet: defer vmalloc TX_RING free until skbs finish").

---
 include/linux/skbuff.h |   19 ---------
 net/packet/af_packet.c |  102 ++++++++++++++++++++++++++++++-------------------
 2 files changed, 65 insertions(+), 56 deletions(-)
include/linux/skbuff.h +1 -18
@@ -1834,22 +1834,6 @@ static inline void skb_zcopy_set(struct
 	}
 }
 
-static inline void skb_zcopy_set_nouarg(struct sk_buff *skb, void *val)
-{
-	skb_shinfo(skb)->destructor_arg = (void *)((uintptr_t) val | 0x1UL);
-	skb_shinfo(skb)->flags |= SKBFL_ZEROCOPY_FRAG;
-}
-
-static inline bool skb_zcopy_is_nouarg(struct sk_buff *skb)
-{
-	return (uintptr_t) skb_shinfo(skb)->destructor_arg & 0x1UL;
-}
-
-static inline void *skb_zcopy_get_nouarg(struct sk_buff *skb)
-{
-	return (void *)((uintptr_t) skb_shinfo(skb)->destructor_arg & ~0x1UL);
-}
-
 static inline void net_zcopy_put(struct ubuf_info *uarg)
 {
 	if (uarg)
@@ -1872,8 +1856,7 @@ static inline void skb_zcopy_clear(struc
 	struct ubuf_info *uarg = skb_zcopy(skb);
 
 	if (uarg) {
-		if (!skb_zcopy_is_nouarg(skb))
-			uarg->ops->complete(skb, uarg, zerocopy_success);
+		uarg->ops->complete(skb, uarg, zerocopy_success);
 
 		skb_shinfo(skb)->flags &= ~SKBFL_ALL_ZEROCOPY;
 	}
net/packet/af_packet.c +64 -38

158 lines not on this page · raw message

author maintainer
#322 lore raw

PATCH 322/457 HID: alps: unregister DualPoint Stick input device on remove

In reply to Greg Kroah-Hartman

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

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

From: Chen Changcheng <[email protected]>

commit aa9dde93e05a837645fdfd577eea71f0733f0694 upstream.

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
@@ -823,6 +823,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) },
@@ -845,6 +863,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
#323 lore raw

PATCH 323/457 HID: alps: fix use-after-free on input2 registration failure

In reply to Greg Kroah-Hartman

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

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

From: Chen Changcheng <[email protected]>

commit d3aba3442798ce4a4c8ce3104d7b286d61e605f9 upstream.

alps_input_configured() stores data->input2 before calling input_register_device(). If registration fails, input_free_device() frees the input device but data->input2 still points to the freed memory. alps_input_configured() calls hid_hw_open() before allocating input2, so URBs are already active and raw_event can fire during the failure window. A U1_SP_ABSOLUTE_REPORT_ID report arriving then causes u1_raw_event() to dereference the freed data->input2 -> use-after-free.

Fix by only storing input2 into drvdata after successful registration and adding a NULL guard in the raw_event path.

Fixes 2562756dde55 ("HID: add Alps I2C HID Touchpad-Stick support")
---
 drivers/hid/hid-alps.c |    6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)
drivers/hid/hid-alps.c +4 -2
@@ -407,6 +407,8 @@ static int u1_raw_event(struct alps_dev
 		return 1;
 
 	case U1_SP_ABSOLUTE_REPORT_ID:
+		if (!hdata->input2)
+			return 0;
 		sp_x = get_unaligned_le16(data+2);
 		sp_y = get_unaligned_le16(data+4);
 
@@ -738,7 +740,6 @@ static int alps_input_configured(struct
 			goto exit;
 		}
 
-		data->input2 = input2;
 		input2->phys = input->phys;
 		input2->name = "DualPoint Stick";
 		input2->id.bustype = BUS_I2C;
@@ -762,11 +763,12 @@ static int alps_input_configured(struct
 		__set_bit(INPUT_PROP_POINTER, input2->propbit);
 		__set_bit(INPUT_PROP_POINTING_STICK, input2->propbit);
 
-		if (input_register_device(data->input2)) {
+		if (input_register_device(input2)) {
 			input_free_device(input2);
 			ret = -ENOENT;
 			goto exit;
 		}
+		data->input2 = input2;
 	}
 
 exit:
author maintainer
#324 lore raw

PATCH 324/457 HID: hid-oxp: use cancel_delayed_work_sync() in remove

In reply to Greg Kroah-Hartman

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

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

From: Tristan Madani <[email protected]>

commit abd24922c2a9797d6184be0561dd85e7bcdfd091 upstream.

oxp_hid_remove() uses cancel_delayed_work() for all three delayed work items. cancel_delayed_work() only dequeues a pending work item without waiting for a currently executing callback to finish. If any of the work callbacks (oxp_rgb_queue_fn, oxp_btn_queue_fn, oxp_mcu_init_fn) is running at the time of removal, the callback continues executing concurrently with hid_hw_close() and hid_hw_stop(), accessing the HID device after it has been closed and stopped.

Use cancel_delayed_work_sync() instead to ensure that any in-progress work callback completes before device teardown proceeds.

---
 drivers/hid/hid-oxp.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)
drivers/hid/hid-oxp.c +3 -3
index d2ded6b08ce9..1e691ebc1199 100644
@@ -1552,9 +1552,9 @@ static int oxp_hid_probe(struct hid_device *hdev,
 
 static void oxp_hid_remove(struct hid_device *hdev)
 {
-	cancel_delayed_work(&drvdata.oxp_rgb_queue);
-	cancel_delayed_work(&drvdata.oxp_btn_queue);
-	cancel_delayed_work(&drvdata.oxp_mcu_init);
+	cancel_delayed_work_sync(&drvdata.oxp_rgb_queue);
+	cancel_delayed_work_sync(&drvdata.oxp_btn_queue);
+	cancel_delayed_work_sync(&drvdata.oxp_mcu_init);
 	hid_hw_close(hdev);
 	hid_hw_stop(hdev);
 }
Signature · 1 line
2.55.0
author maintainer
#325 lore raw

PATCH 325/457 HID: quirks: add ALWAYS_POLL quirk for SDINNOVATION gaming keyboard

In reply to Greg Kroah-Hartman

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

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

From: Junjie Cao <[email protected]>

commit cdb669a3b8f844aca71fc3224990157d61562165 upstream.

The SDINNOVATION gaming keyboard (USB ID 36ae:feab) stops reporting input events after its RGB lighting mode is switched about twice. Disabling USB autosuspend and unbinding the other HID interfaces make no difference; the issue does not occur on Windows.

HID_QUIRK_ALWAYS_POLL alone resolves it, verified on 7.1.8 via usbhid.quirks=0x36ae:0xfeab:0x400.

---
 drivers/hid/hid-ids.h    |    3 +++
 drivers/hid/hid-quirks.c |    1 +
 2 files changed, 4 insertions(+)
drivers/hid/hid-ids.h +3 -0
@@ -1283,6 +1283,9 @@
 #define USB_DEVICE_ID_SAMSUNG_WIRELESS_UNIVERSAL_KBD	0xa006
 #define USB_DEVICE_ID_SAMSUNG_WIRELESS_MULTI_HOGP_KBD	0xa064
 
+#define USB_VENDOR_ID_SDINNOVATION		0x36ae
+#define USB_DEVICE_ID_SDINNOVATION_GAMING_KBD	0xfeab
+
 #define USB_VENDOR_ID_SEMICO			0x1a2c
 #define USB_DEVICE_ID_SEMICO_USB_KEYKOARD	0x0023
 #define USB_DEVICE_ID_SEMICO_USB_KEYKOARD2	0x0027
drivers/hid/hid-quirks.c +1 -0
@@ -183,6 +183,7 @@ static const struct hid_device_id hid_qu
 	{ HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_X52_2), HID_QUIRK_INCREMENT_USAGE_ON_DUPLICATE },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_X52_PRO), HID_QUIRK_INCREMENT_USAGE_ON_DUPLICATE },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_X65), HID_QUIRK_INCREMENT_USAGE_ON_DUPLICATE },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_SDINNOVATION, USB_DEVICE_ID_SDINNOVATION_GAMING_KBD), HID_QUIRK_ALWAYS_POLL },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_SEMICO, USB_DEVICE_ID_SEMICO_USB_KEYKOARD2), HID_QUIRK_NO_INIT_REPORTS },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_SEMICO, USB_DEVICE_ID_SEMICO_USB_KEYKOARD), HID_QUIRK_NO_INIT_REPORTS },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_SENNHEISER, USB_DEVICE_ID_SENNHEISER_BTD500USB), HID_QUIRK_NOGET },
author maintainer
#326 lore raw

PATCH 326/457 HID: wacom: fix OOB read in wacom_wac_pen_serial_enforce()

In reply to Greg Kroah-Hartman

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

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

From: Wei Jie LAW <[email protected]>

commit 9aa237cf66495b2426ddde8532e9b08a0ed83aaa upstream.

The 'wacom_wac_pen_serial_enforce()' function may calculate and pass an invalid offset to hid_field_extract(), resulting in memory reads at incorrect addresses -- possibly beyond the end of the report. If a field in the HID descriptor lists more usages than its Report Count actually reserves space for, the function's inner 'j' will walk past the end of the field:

	for (i = 0; i < report->maxfield; i++) {
		for (j = 0; j < report->field[i]->maxusage; j++) {
			...
			value = hid_field_extract(hdev, raw_data + 1,
						  offset + j * size, size);

A descriptor listing 12288 usages against Report Count 1 has the loop extract the usage at index 12287 from bit offset 98296 -- about 12 KB past a 2-byte received report. The value is stored in wacom_wac->serial[0] and can reach userspace as an MSC_SERIAL event, making this an information disclosure.

Clamp the loop to field->report_count, the number of value slots the report holds. Value slots past the last declared usage are still scanned; they reuse that usage (HID 1.11, 6.2.2.8).

Verified on v6.12.105 with a UHID reproducer: a 2-byte report from such a descriptor trips KASAN before the patch and not after it.

Fixes 83417206427b ("HID: wacom: Queue events with missing type/serial data for later processing")
Assisted-by Claude:claude-opus-5
Assisted-by GLM:glm-5.3
---
 drivers/hid/wacom_sys.c |    5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)
drivers/hid/wacom_sys.c +3 -2
@@ -113,8 +113,9 @@ static int wacom_wac_pen_serial_enforce(
 
 	/* Queue events which have invalid tool type or serial number */
 	for (i = 0; i < report->maxfield; i++) {
-		for (j = 0; j < report->field[i]->maxusage; j++) {
-			struct hid_field *field = report->field[i];
+		struct hid_field *field = report->field[i];
+
+		for (j = 0; j < field->report_count; j++) {
 			struct hid_usage *usage = &field->usage[j];
 			unsigned int equivalent_usage = wacom_equivalent_usage(usage->hid);
 			unsigned int offset;
author maintainer
#327 lore raw

PATCH 327/457 net/mlx5: Fix rev_entry reference leak in mlx5_tc_ct_shared_counter_get()

In reply to Greg Kroah-Hartman

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

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

From: Wentao Liang <[email protected]>

commit 0bf6bb567f0edaa771e7dd208ef98da50e6a4485 upstream.

When the reverse entry is found but its counter is already being released, refcount_inc_not_zero() fails and the reference taken by mlx5_tc_ct_entry_get() is never dropped before falling through to create_counter. Drop it so the reverse entry is not kept alive forever by a shared counter lookup that did not use it.

Fixes 1edae2335adf ("net/mlx5e: CT: Use the same counter for both directions")
---
 drivers/net/ethernet/mellanox/mlx5/core/en/tc_ct.c |    3 +++
 1 file changed, 3 insertions(+)
drivers/net/ethernet/mellanox/mlx5/core/en/tc_ct.c +3 -0
@@ -1082,6 +1082,9 @@ mlx5_tc_ct_shared_counter_get(struct mlx
 
 	spin_unlock_bh(&ct_priv->ht_lock);
 
+	if (rev_entry)
+		mlx5_tc_ct_entry_put(rev_entry);
+
 create_counter:
 
 	shared_counter = mlx5_tc_ct_counter_create(ct_priv);
author maintainer
#328 lore raw

PATCH 328/457 net/mlx5e: advertise MACsec offload only when supported

In reply to Greg Kroah-Hartman

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

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

From: Ralf Lici <[email protected]>

commit 4581c3d2adc3c73a019bc38db64ca11f28bbd7fd upstream.

Commit 339ccec8d43d ("net/mlx5: Enable MACsec offload feature for VLAN interface") added NETIF_F_HW_MACSEC unconditionally to vlan_features so that VLAN devices could inherit MACsec offload support.

mlx5e_build_nic_netdev subsequently copies vlan_features into hw_features and features. As a result, all mlx5e NIC netdevices advertise MACsec hardware offload, even when the firmware does not support it and the driver does not install macsec_ops.

Set the MACsec feature bits in mlx5e_macsec_build_netdev, after device capabilities have been validated. This preserves MACsec-over-VLAN support and the ethtool feature control on capable devices, without advertising either on unsupported hardware.

Fixes 339ccec8d43d ("net/mlx5: Enable MACsec offload feature for VLAN interface")
---
 drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec.c |    2 ++
 drivers/net/ethernet/mellanox/mlx5/core/en_main.c         |    1 -
 2 files changed, 2 insertions(+), 1 deletion(-)
drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec.c +2 -0
@@ -1724,6 +1724,8 @@ void mlx5e_macsec_build_netdev(struct ml
 	mlx5_core_dbg(priv->mdev, "mlx5e: MACsec acceleration enabled\n");
 	netdev->macsec_ops = &macsec_offload_ops;
 	netdev->features |= NETIF_F_HW_MACSEC;
+	netdev->hw_features |= NETIF_F_HW_MACSEC;
+	netdev->vlan_features |= NETIF_F_HW_MACSEC;
 	netif_keep_dst(netdev);
 }
 
drivers/net/ethernet/mellanox/mlx5/core/en_main.c +0 -1
@@ -5851,7 +5851,6 @@ static void mlx5e_build_nic_netdev(struc
 
 	netdev->vlan_features    |= NETIF_F_SG;
 	netdev->vlan_features    |= NETIF_F_HW_CSUM;
-	netdev->vlan_features    |= NETIF_F_HW_MACSEC;
 	netdev->vlan_features    |= NETIF_F_GRO;
 	netdev->vlan_features    |= NETIF_F_TSO;
 	netdev->vlan_features    |= NETIF_F_TSO6;
author maintainer
#329 lore raw

PATCH 329/457 net/mlx5e: fix swapped IPv6 IPsec policy masks

In reply to Greg Kroah-Hartman

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

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

From: Andrea Parri <[email protected]>

commit 10de7ed8ef4840da9ca21de4c29578657ac367db upstream.

IPv6 XFRM policies may use different source and destination prefix lengths. mlx5e_ipsec_policy_mask() builds the corresponding masks independently, but setup_fte_addr6() installs each mask in the opposite address field.

When the prefix lengths differ, this makes the source match use the destination prefix and the destination match use the source prefix. The resulting hardware rule can both miss traffic covered by the policy and match traffic outside it.

Install each mask in its corresponding match field.

---
 drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c +2 -2
@@ -1564,14 +1564,14 @@ static void setup_fte_addr6(struct mlx5_
 		memcpy(MLX5_ADDR_OF(fte_match_param, spec->match_value,
 				    outer_headers.src_ipv4_src_ipv6.ipv6_layout.ipv6), saddr, 16);
 		memcpy(MLX5_ADDR_OF(fte_match_param, spec->match_criteria,
-				    outer_headers.src_ipv4_src_ipv6.ipv6_layout.ipv6), dmask, 16);
+				    outer_headers.src_ipv4_src_ipv6.ipv6_layout.ipv6), smask, 16);
 	}
 
 	if (!addr6_all_zero(daddr)) {
 		memcpy(MLX5_ADDR_OF(fte_match_param, spec->match_value,
 				    outer_headers.dst_ipv4_dst_ipv6.ipv6_layout.ipv6), daddr, 16);
 		memcpy(MLX5_ADDR_OF(fte_match_param, spec->match_criteria,
-				    outer_headers.dst_ipv4_dst_ipv6.ipv6_layout.ipv6), smask, 16);
+				    outer_headers.dst_ipv4_dst_ipv6.ipv6_layout.ipv6), dmask, 16);
 	}
 }
 
author maintainer
#330 lore raw

PATCH 330/457 net/sched: reject IDR error pointers when deleting actions

In reply to Greg Kroah-Hartman

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

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

From: Weiming Shi <[email protected]>

commit c82b797abe668d0b668601a93ba2c0b071a63574 upstream.

tcf_action_delete() drops the reference held by its lookup before calling tcf_idr_delete_index() with the saved action index. An unlocked classifier can remove that action and reserve the same IDR slot with ERR_PTR(-EBUSY) in between.

tcf_idr_delete_index() only checks the lookup result for NULL. It therefore treats the reservation as a tc_action and dereferences tcfa_bindcnt. A hardware execution breakpoint was used to schedule the interleaving without changing the kernel source. KASAN reported this decoded trace:

  BUG: KASAN: null-ptr-deref in tca_action_gd+0x5b9/0x1010
  Read of size 4 at addr 0000000000000010 by task poc/150
  Oops: general protection fault, probably for non-canonical address 0xdffffc0000000002
  RIP: tca_action_gd+0x5c0/0x1010:
    arch_atomic_read at arch/x86/include/asm/atomic.h:23
    raw_atomic_read at include/linux/atomic/atomic-arch-fallback.h:457
    atomic_read at include/linux/atomic/atomic-instrumented.h:33
    tcf_idr_delete_index at net/sched/act_api.c:766
    tcf_action_delete at net/sched/act_api.c:1859
    tcf_del_notify at net/sched/act_api.c:2014
    tca_action_gd at net/sched/act_api.c:2064
  R13: 0000000000000010 R15: fffffffffffffff0
  Kernel panic - not syncing: Fatal exception

R15 contains ERR_PTR(-EBUSY), and adding the tcfa_bindcnt offset produces the address in R13. With the guard applied, the same reproducer returned -ENOENT without a KASAN report or panic. Treat error pointers as absent and return -ENOENT.

---
 net/sched/act_api.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
net/sched/act_api.c +1 -1
@@ -758,7 +758,7 @@ static int tcf_idr_delete_index(struct t
 
 	mutex_lock(&idrinfo->lock);
 	p = idr_find(&idrinfo->action_idr, index);
-	if (!p) {
+	if (IS_ERR_OR_NULL(p)) {
 		mutex_unlock(&idrinfo->lock);
 		return -ENOENT;
 	}
author maintainer
#331 lore raw

PATCH 331/457 net: airoha: npu: cancel wdt_work after releasing the WDT IRQ

In reply to Greg Kroah-Hartman

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

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

From: Myeonghun Pak <[email protected]>

commit 4bdee8060d1e4581624e68fbd369b1afb14df4bc upstream.

airoha_npu_remove() calls cancel_work_sync() on each core's wdt_work, but the watchdog IRQ that queues it is requested with devm_request_irq() and is freed only after .remove() returns. airoha_npu_wdt_handler() can therefore schedule_work() again once the cancel has returned. struct airoha_npu, which contains the work, is devm_kzalloc()'d and is freed in that same unwind, so the late work dereferences freed memory.

Register the work with devm_work_autocancel() before devm_request_irq() and drop .remove(). Devres runs in reverse order, so the IRQ is freed before cancel_work_sync(), including when probe fails. A cancel left in .remove() cannot get that order. Initializing the work first also stops a pending watchdog interrupt from queuing an uninitialized work item. Probe currently calls INIT_WORK() only after devm_request_irq().

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

---
 drivers/net/ethernet/airoha/airoha_npu.c |   18 ++++++------------
 1 file changed, 6 insertions(+), 12 deletions(-)
drivers/net/ethernet/airoha/airoha_npu.c +6 -12
@@ -5,6 +5,7 @@
  */
 
 #include <linux/devcoredump.h>
+#include <linux/devm-helpers.h>
 #include <linux/firmware.h>
 #include <linux/platform_device.h>
 #include <linux/of_net.h>
@@ -749,12 +750,15 @@ static int airoha_npu_probe(struct platf
 		if (irq < 0)
 			return irq;
 
+		err = devm_work_autocancel(dev, &core->wdt_work,
+					   airoha_npu_wdt_work);
+		if (err)
+			return err;
+
 		err = devm_request_irq(dev, irq, airoha_npu_wdt_handler,
 				       IRQF_SHARED, "airoha-npu-wdt", core);
 		if (err)
 			return err;
-
-		INIT_WORK(&core->wdt_work, airoha_npu_wdt_work);
 	}
 
 	/* wlan IRQ lines */
@@ -801,18 +805,8 @@ static int airoha_npu_probe(struct platf
 	return 0;
 }
 
-static void airoha_npu_remove(struct platform_device *pdev)
-{
-	struct airoha_npu *npu = platform_get_drvdata(pdev);
-	int i;
-
-	for (i = 0; i < ARRAY_SIZE(npu->cores); i++)
-		cancel_work_sync(&npu->cores[i].wdt_work);
-}
-
 static struct platform_driver airoha_npu_driver = {
 	.probe = airoha_npu_probe,
-	.remove = airoha_npu_remove,
 	.driver = {
 		.name = "airoha-npu",
 		.of_match_table = of_airoha_npu_match,
author maintainer
#332 lore raw

PATCH 332/457 net: arp: terminate device name before lookup

In reply to Greg Kroah-Hartman

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

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

From: Zijie Huang <[email protected]>

commit d8b6529e80bcb4fb8177121404cbb3377acaebd2 upstream.

The ARP ioctl copies a user-provided struct arpreq into a stack object. Its arp_dev field may contain IFNAMSIZ bytes without a NUL terminator.

Such input is passed to dev_get_by_name_rcu() or __dev_get_by_name(), where strcmp() can read past the end of the stack object when a matching alternative interface name exists.

Terminate the field before the lookup to prevent the out-of-bounds read.

---
 net/ipv4/arp.c |    1 +
 1 file changed, 1 insertion(+)
net/ipv4/arp.c +1 -0
@@ -1278,6 +1278,7 @@ int arp_ioctl(struct net *net, unsigned
 		err = copy_from_user(&r, arg, sizeof(struct arpreq));
 		if (err)
 			return -EFAULT;
+		r.arp_dev[IFNAMSIZ - 1] = '\0';
 		break;
 	default:
 		return -EINVAL;
author maintainer
#333 lore raw

PATCH 333/457 net: atl1: fix soft lockup on out-of-range cmb_tpd_next_to_clean read

In reply to Greg Kroah-Hartman

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

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

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

commit 43e746821f5f5afbbf68e388bf9fbe221e03bfca 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 cmb_tpd_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.

---
 drivers/net/ethernet/atheros/atlx/atl1.c |    3 +++
 1 file changed, 3 insertions(+)
drivers/net/ethernet/atheros/atlx/atl1.c +3 -0
@@ -2066,6 +2066,9 @@ static int atl1_intr_tx(struct atl1_adap
 	sw_tpd_next_to_clean = atomic_read(&tpd_ring->next_to_clean);
 	cmb_tpd_next_to_clean = le16_to_cpu(adapter->cmb.cmb->tpd_cons_idx);
 
+	if (unlikely(cmb_tpd_next_to_clean >= tpd_ring->count))
+		cmb_tpd_next_to_clean = sw_tpd_next_to_clean;
+
 	while (cmb_tpd_next_to_clean != sw_tpd_next_to_clean) {
 		buffer_info = &tpd_ring->buffer_info[sw_tpd_next_to_clean];
 		if (buffer_info->dma) {
author maintainer
#334 lore raw

PATCH 334/457 net: hisilicon: hns_dsaf_mac: fix mdio device leak in hns_mac_register_phy()

In reply to Greg Kroah-Hartman

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

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

From: Wentao Liang <[email protected]>

commit 999e8295bc41d6ce45b8e54f88150efa96f3f01e upstream.

hns_dsaf_find_platform_device() returns the mdio platform device with its reference count incremented. hns_mac_register_phy() never drops that reference, so the mdio device can not be released.

Release the reference on both the deferred probe and the normal path.

Fixes 1d1afa2ebf82 ("net: hns: register phy device in each mac initial sequence")
---
 drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c |    3 +++
 1 file changed, 3 insertions(+)
drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c +3 -0
@@ -793,6 +793,7 @@ static int hns_mac_register_phy(struct h
 		dev_err(mac_cb->dev,
 			"mac%d mdio is NULL, dsaf will probe again later\n",
 			mac_cb->mac_id);
+		put_device(&pdev->dev);
 		return -EPROBE_DEFER;
 	}
 
@@ -801,6 +802,8 @@ static int hns_mac_register_phy(struct h
 		dev_dbg(mac_cb->dev, "mac%d register phy addr:%d\n",
 			mac_cb->mac_id, addr);
 
+	put_device(&pdev->dev);
+
 	return rc;
 }
 
author maintainer
#335 lore raw

PATCH 335/457 net: ipconfig: bound DHCP option construction

In reply to Greg Kroah-Hartman

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

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

From: Yuqi Xu <[email protected]>

commit e47a1958e12abc3a17b5231a4f21c8f1bf662e08 upstream.

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
#336 lore raw

PATCH 336/457 net: macb: fix dma_alloc_coherent() leak on macb_alloc() error paths

In reply to Greg Kroah-Hartman

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

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

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

commit 23d42b9a3bcd55b17d3b371544fedc708c2397e9 upstream.

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.

Fixes 78d901897b3c ("net: macb: single dma_alloc_coherent() for DMA descriptors")
---
 drivers/net/ethernet/cadence/macb_main.c |   14 ++++++++++++--
 1 file changed, 12 insertions(+), 2 deletions(-)
drivers/net/ethernet/cadence/macb_main.c +12 -2
@@ -2756,14 +2756,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->netdev, "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->netdev, "Allocated %zu bytes for %u RX rings at %08lx (mapped %p)\n",
 		   size, bp->num_queues, (unsigned long)rx_dma, rx);
author maintainer
#337 lore raw

PATCH 337/457 net: openvswitch: conntrack: avoid modifying shared unconfirmed ct entry

In reply to Greg Kroah-Hartman

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

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

From: Ilya Maximets <[email protected]>

commit 26b2bd70d22457556e2fa01cbf1192cb1a94d619 upstream.

In a case where skb with an unconfirmed ct entry gets cloned, we may end up committing both 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 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 committing both clones without modifications into the same zone is needed. So, let's just reset the entry in case for some reason we got an skb with a shared one during commit. This doesn't affect any known use cases, but avoids any potential problems with sharing and modification of the unconfirmed ct entry.

The fixes tag points to the introduction of helpers, since that's the main UAF trigger for the sharing.

---
 include/net/netfilter/nf_conntrack.h |    5 +++++
 net/openvswitch/conntrack.c          |   12 ++++++++++++
 2 files changed, 17 insertions(+)
include/net/netfilter/nf_conntrack.h +5 -0
@@ -185,6 +185,11 @@ static inline void nf_ct_put(struct nf_c
 		nf_ct_destroy(&ct->ct_general);
 }
 
+static inline bool nf_ct_shared(const struct nf_conn *ct)
+{
+	return refcount_read(&ct->ct_general.use) > 1;
+}
+
 /* load module; enable/disable conntrack in this namespace */
 int nf_ct_netns_get(struct net *net, u8 nfproto);
 void nf_ct_netns_put(struct net *net, u8 nfproto);
net/openvswitch/conntrack.c +12 -0
@@ -734,6 +734,18 @@ static int __ovs_ct_lookup(struct net *n
 	enum ip_conntrack_info ctinfo;
 	struct nf_conn *ct;
 
+	/* 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 the commit as we
+	 * must not modify the extension set.  Reset.
+	 */
+	if (cached && info->commit) {
+		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) {
 		struct nf_hook_state state = {
 			.hook = NF_INET_PRE_ROUTING,
author maintainer
#338 lore raw

PATCH 338/457 net: openvswitch: conntrack: remove add_helper dead code

In reply to Greg Kroah-Hartman

7.2-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
#339 lore raw

PATCH 339/457 net: openvswitch: conntrack: fix helper UAF due to extensions realloc

In reply to Greg Kroah-Hartman

7.2-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);
@@ -1026,6 +1029,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
#340 lore raw

PATCH 340/457 net: atl1c: fix soft lockup on out-of-range tpd_cons read

In reply to Greg Kroah-Hartman

7.2-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
#341 lore raw

PATCH 341/457 net: atl1e: fix soft lockup on out-of-range hw_next_to_clean read

In reply to Greg Kroah-Hartman

7.2-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
#342 lore raw

PATCH 342/457 net: bridge: mdb: restart port group walk after deletion

In reply to Greg Kroah-Hartman

7.2-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
#343 lore raw

PATCH 343/457 net: ena: fix PHC cleanup on probe failure

In reply to Greg Kroah-Hartman

7.2-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
#344 lore raw

PATCH 344/457 net: ena: fix MMIO read buffer leak on probe failure

In reply to Greg Kroah-Hartman

7.2-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
#345 lore raw

PATCH 345/457 net: phy: intel-xway: workaround 100BASE-TX Link-Up issue

In reply to Greg Kroah-Hartman

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

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

From: Alexander Sverdlin <[email protected]>

commit b94773dc4df7026a6f29b2c65e96e88d29cdb576 upstream.

MaxLinear GSW12x/GSW14x Ethernet Switch Errata Sheet states:
"An issue has been sporadically observed after device power-on on the first link-up attempt in 100BASE-TX mode resulting in either the link-up taking a long time, or failing to link-up altogether...

Workaround:
After power-on, enable Cable Diagnostic Mode for all ports and disable it..."

Implement the proposed workaround unconditionally in the Intel XWAY driver (MaxLinear GSW1xx switches incorporate Intel XWAY PHYs) because the diagnostic bits have the same meaning even in older integral PHYs such as GPY111/PEF7071/PHY11G. So it's not clear how to distinguish the affected newer integrated PHYs, but the workaround should not hurt the older PHYs.

---
 drivers/net/phy/intel-xway.c |   29 ++++++++++++++++++++++++++++-
 1 file changed, 28 insertions(+), 1 deletion(-)
drivers/net/phy/intel-xway.c +28 -1
@@ -16,6 +16,11 @@
 #define XWAY_MDIO_ISTAT			0x1A	/* interrupt status */
 #define XWAY_MDIO_LED			0x1B	/* led control */
 
+#define XWAY_MDIO_GCTRL_TM_MASK		GENMASK(15, 13)
+#define XWAY_MDIO_GCTRL_TM(mode)	FIELD_PREP(XWAY_MDIO_GCTRL_TM_MASK, (mode))
+#define XWAY_MDIO_GCTRL_TM_NOP		XWAY_MDIO_GCTRL_TM(0)	/* Normal operation */
+#define XWAY_MDIO_GCTRL_TM_CDIAG	XWAY_MDIO_GCTRL_TM(6)	/* Cable diagnostics */
+
 #define XWAY_MDIO_ERRCNT_SEL		GENMASK(11, 8)
 #define XWAY_MDIO_ERRCNT_COUNT		GENMASK(7, 0)
 #define XWAY_MDIO_ERRCNT_SEL_RXERR	0
@@ -326,6 +331,28 @@ static int xway_gphy_probe(struct phy_de
 	return 0;
 }
 
+static int xway_11g_int_config_init(struct phy_device *phydev)
+{
+	int err;
+
+	/* An issue has been sporadically observed after device power-on on the
+	 * first link-up attempt in 100BASE-TX mode resulting in either the
+	 * link-up taking a long time, or failing to link-up altogether.
+	 *
+	 * Workaround:
+	 * After power-on, enable Cable Diagnostic Mode for all ports and
+	 * disable it.
+	 */
+	err = phy_modify(phydev, MII_CTRL1000, XWAY_MDIO_GCTRL_TM_MASK, XWAY_MDIO_GCTRL_TM_CDIAG);
+	if (err)
+		return err;
+	err = phy_modify(phydev, MII_CTRL1000, XWAY_MDIO_GCTRL_TM_MASK, XWAY_MDIO_GCTRL_TM_NOP);
+	if (err)
+		return err;
+
+	return xway_gphy_config_init(phydev);
+}
+
 static int xway_gphy14_config_aneg(struct phy_device *phydev)
 {
 	int reg, err;
@@ -735,7 +762,7 @@ static struct phy_driver xway_gphy[] = {
 		.phy_id_mask	= 0xffffffff,
 		.name		= "Intel XWAY PHY11G (xRX v1.2 integrated)",
 		/* PHY_GBIT_FEATURES */
-		.config_init	= xway_gphy_config_init,
+		.config_init	= xway_11g_int_config_init,
 		.probe		= xway_gphy_probe,
 		.handle_interrupt = xway_gphy_handle_interrupt,
 		.config_intr	= xway_gphy_config_intr,
author maintainer
#346 lore raw

PATCH 346/457 net: phy: micrel: Advance register data pointer in write loop

In reply to Greg Kroah-Hartman

7.2-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
@@ -6285,6 +6285,7 @@ static int lanphy_write_reg_data(struct
 					    data->val);
 		if (ret)
 			break;
+		data++;
 	}
 
 	return ret;
author maintainer
#347 lore raw

PATCH 347/457 net: txgbe: fix FDIR filter restore for VF rules

In reply to Greg Kroah-Hartman

7.2-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
#348 lore raw

PATCH 348/457 net: usb: cdc_mbim: add MeiG Smart SRM821 to ZLP whitelist

In reply to Greg Kroah-Hartman

7.2-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
@@ -635,6 +635,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
#349 lore raw

PATCH 349/457 net: usb: lan78xx: Fix URB reference leak in lan78xx_submit_deferred_urbs()

In reply to Greg Kroah-Hartman

7.2-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
@@ -5239,10 +5239,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
#350 lore raw

PATCH 350/457 netfilter: ip6t_rpfilter: reject routes without inet6_dev

In reply to Greg Kroah-Hartman

7.2-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
#351 lore raw

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

In reply to Greg Kroah-Hartman

7.2-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
#352 lore raw

PATCH 352/457 netfilter: nf_tables: skip expired catchall elements on insert and delete

In reply to Greg Kroah-Hartman

7.2-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
@@ -6990,11 +6990,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;
 		}
@@ -7087,11 +7090,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
#353 lore raw

PATCH 353/457 nfc: fix use-after-free in nfc_get_local_general_bytes

In reply to Greg Kroah-Hartman

7.2-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
#354 lore raw

PATCH 354/457 nfc: llcp: drop truncated I/RR/RNR PDUs in nfc_llcp_recv_hdlc()

In reply to Greg Kroah-Hartman

7.2-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
#355 lore raw

PATCH 355/457 nfc: port100: reject frames whose declared length exceeds the received data

In reply to Greg Kroah-Hartman

7.2-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
#356 lore raw

PATCH 356/457 nfc: trf7970a: power down on startup RX gain failure

In reply to Greg Kroah-Hartman

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

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

From: Myeonghun Pak <[email protected]>

commit 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
#357 lore raw

PATCH 357/457 scsi: libiscsi_tcp: Check the data direction of a Data-In PDU

In reply to Greg Kroah-Hartman

7.2-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
#358 lore raw

PATCH 358/457 scsi: ufs: core: Keep internal commands dispatchable during error handling

In reply to Greg Kroah-Hartman

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

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

From: Stanley Jhu <[email protected]>

commit b52d695d062095327b944acf7daabbc816ab319b upstream.

Commit 08b12cda6c44 ("scsi: ufs: core: Switch to scsi_get_internal_cmd()") switched UFS internal commands to allocate requests on hba->host->pseudo_sdev->request_queue, which shares the host tagset with regular LUNs.

During error recovery, ufshcd_err_handling_prepare() calls blk_mq_quiesce_tagset(&hba->host->tag_set), marking all queues in the tagset as quiesced, including pseudo_sdev->request_queue. When ufshcd_verify_dev_init() subsequently issues internal commands (e.g. NOP OUT UPIU) via blk_execute_rq(), blk_mq_run_hw_queue() skips running the quiesced queue, resulting in an unrecoverable circular wait deadlock.

Keep quiescing the tagset and unquiesce the pseudo SCSI device on top of that, so internal commands stay dispatchable while the logical units remain quiesced. Re-quiesce the pseudo device before unquiescing the tagset so that quiesce_depth stays balanced.

Clock scaling and ufshcd_pause_command_processing() are unaffected: they keep quiescing the whole tagset, internal commands included.

---
 drivers/ufs/core/ufshcd.c |    6 ++++++
 1 file changed, 6 insertions(+)
drivers/ufs/core/ufshcd.c +6 -0
@@ -6748,11 +6748,17 @@ static void ufshcd_err_handling_prepare(
 	}
 	/* Wait for ongoing ufshcd_queuecommand() calls to finish. */
 	blk_mq_quiesce_tagset(&hba->host->tag_set);
+	/*
+	 * Internal commands are submitted on the pseudo SCSI device. Let them
+	 * through so that the error handler can recover the link.
+	 */
+	blk_mq_unquiesce_queue(hba->host->pseudo_sdev->request_queue);
 	cancel_work_sync(&hba->eeh_work);
 }
 
 static void ufshcd_err_handling_unprepare(struct ufs_hba *hba)
 {
+	blk_mq_quiesce_queue_nowait(hba->host->pseudo_sdev->request_queue);
 	blk_mq_unquiesce_tagset(&hba->host->tag_set);
 	ufshcd_release(hba);
 	if (ufshcd_is_clkscaling_supported(hba))
author maintainer
#359 lore raw

PATCH 359/457 scsi: ufs: pltfrm: Add quirk for R-Car S4 lacking lanes-per-direction

In reply to Greg Kroah-Hartman

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

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

From: Geert Uytterhoeven <[email protected]>

commit c9ee6511332687ea714ad8ab86a53cb837d86eea upstream.

Since commit e72323f3b09f ("scsi: ufs: core: Configure only active lanes during link"), the following error is observed on R-Car S4:

ufshcd-renesas e6860000.ufs: Tx lane mismatch [config,reported] [2,1] ufshcd-renesas e6860000.ufs: link startup failed -67
ufshcd-renesas e6860000.ufs: error -ENOLINK: Initialization failed with error -67 ufshcd-renesas e6860000.ufs: probe with driver ufshcd-renesas failed with error -67

R-Car S4 has one UFS lane per direction, as described in section 152.1 of its hardware manual. Without lanes-per-direction, the UFS platform driver defaults to two lanes.

Previously, the core used PA_CONNECTEDRXDATALANES and PA_CONNECTEDTXDATALANES to configure the link without checking them against lanes-per-direction, so the missing property did not prevent initialization.

While fixing the R-Car S4 DTS is the proper solution, doing only that would still break backwards compatibility with existing DTBs. Hence add a quirk to let lanes-per-direction default to one on R-Car S4.

---
 drivers/ufs/host/ufshcd-pltfrm.c |    6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)
drivers/ufs/host/ufshcd-pltfrm.c +5 -1
@@ -206,7 +206,11 @@ static void ufshcd_init_lanes_per_dir(st
 		dev_dbg(hba->dev,
 			"%s: failed to read lanes-per-direction, ret=%d\n",
 			__func__, ret);
-		hba->lanes_per_direction = UFSHCD_DEFAULT_LANES_PER_DIRECTION;
+		/* Old R-Car S4 DTBs lack "lanes-per-direction = <1>" */
+		if (of_device_is_compatible(dev->of_node, "renesas,r8a779f0-ufs"))
+			hba->lanes_per_direction = 1;
+		else
+			hba->lanes_per_direction = UFSHCD_DEFAULT_LANES_PER_DIRECTION;
 	}
 }
 
author maintainer
#360 lore raw

PATCH 360/457 parisc: Increase kernel stack size to 32kb

In reply to Greg Kroah-Hartman

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

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

From: Helge Deller <[email protected]>

commit 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
#361 lore raw

PATCH 361/457 parisc: parse early parameters in setup_arch()

In reply to Greg Kroah-Hartman

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

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

From: Zhenghui Hao <[email protected]>

commit 289e99e7a263c6fd6a5d07d6d8f2156b70f3a9d5 upstream.

parisc is one of the few architectures that does not call parse_early_param() from setup_arch(). That was mostly harmless until commit d49004c5f0c1 ("arch, mm: consolidate initialization of nodes, zones and memory map") moved the consumer of several hugetlb command line parameters into mm_core_init_early(), which runs before the generic parse_early_param() call in start_kernel().

As a result hugepages=, hugepagesz=, default_hugepagesz=, hugetlb_cma= and hugetlb_free_vmemmap= are recorded after they have already been consumed and are silently dropped on parisc.

Call parse_early_param() from setup_arch(), after the command line has been set up and the memory inventory has been taken. jump_label_init() must be called first because early parameter handlers may enable or disable static keys. Both functions are safe to call more than once:
the generic calls in start_kernel() remain in place and turn into no-ops.

Fixes d49004c5f0c1 ("arch, mm: consolidate initialization of nodes, zones and memory map")
---
 arch/parisc/kernel/setup.c | 12 ++++++++++++
 1 file changed, 12 insertions(+)
arch/parisc/kernel/setup.c +12 -0
index d3e17a7a8901..4d3015a411d6 100644
@@ -132,6 +132,18 @@ void __init setup_arch(char **cmdline_p)
 	parisc_cache_init();
 	paging_init();
 
+	/*
+	 * Parse early parameters before mm_core_init_early() runs.
+	 * Several early_param() handlers only record data that is consumed
+	 * from there - for example hugepages=, hugepagesz=,
+	 * default_hugepagesz=, hugetlb_cma= and hugetlb_free_vmemmap= - so
+	 * the generic parse_early_param() call in start_kernel() is too late
+	 * for them.  jump_label_init() must come first, since early param
+	 * handlers may enable or disable static keys.
+	 */
+	jump_label_init();
+	parse_early_param();
+
 #ifdef CONFIG_PA11
 	dma_ops_init();
 #endif
Signature · 1 line
2.55.0
author maintainer
#362 lore raw

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

In reply to Greg Kroah-Hartman

7.2-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
@@ -3914,7 +3914,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
#363 lore raw

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

In reply to Greg Kroah-Hartman

7.2-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
@@ -3764,6 +3764,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);
 	}
@@ -3924,15 +3927,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
#364 lore raw

PATCH 364/457 perf/x86/intel: Fix CMT PEBS load/store direction for latency events, to fix sample classification

In reply to Greg Kroah-Hartman

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

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

From: Dapeng Mi <[email protected]>

commit e961d6db42d1f1b66c81438969a648f08573bd9c upstream.

The same bug exists on Crestmont as on Gracemont:
intel_cmt_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 cmt_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_cmt_pebs_event_constraints[] for:

- MEM_UOPS_RETIRED.LOAD_LATENCY
- MEM_UOPS_RETIRED.STORE_LATENCY

This fixes incorrect STORE sample classification.

Fixes e99fb45436ea ("perf/x86/intel: Update event constraints and cache_extra_regsfor MTL")
Cc <[email protected]> # v7.2+
---
 arch/x86/events/intel/ds.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
arch/x86/events/intel/ds.c +2 -2
@@ -1298,8 +1298,8 @@ struct event_constraint intel_grt_pebs_e
 
 struct event_constraint intel_cmt_pebs_event_constraints[] = {
 	/* Allow all events as PEBS with no flags */
-	INTEL_HYBRID_LAT_CONSTRAINT(0x5d0, 0x3),
-	INTEL_HYBRID_LAT_CONSTRAINT(0x6d0, 0xff),
+	INTEL_HYBRID_LDLAT_CONSTRAINT(0x5d0, 0x3),
+	INTEL_HYBRID_STLAT_CONSTRAINT(0x6d0, 0xff),
 	EVENT_CONSTRAINT_END
 };
 
author maintainer
#365 lore raw

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

In reply to Greg Kroah-Hartman

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

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

From: Dapeng Mi <[email protected]>

commit 89dc568e8c0be60e05e5fcd0b528c79077d7b84b upstream.

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
@@ -455,6 +455,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() &&
@@ -470,7 +471,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)
@@ -1291,8 +1301,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
#366 lore raw

PATCH 366/457 perf/x86/intel: Fix DKT PEBS load/store direction for latency events, to fix sample classification

In reply to Greg Kroah-Hartman

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

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

From: Dapeng Mi <[email protected]>

commit 8302c5f475fa5a4ed36a7d32c7b965023d5d7066 upstream.

Same bug exists on Darkmont as on Gracemont:
intel_dkt_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 cmt_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_dkt_pebs_event_constraints[] for:

- MEM_UOPS_RETIRED.LOAD_LATENCY
- MEM_UOPS_RETIRED.STORE_LATENCY

This fixes incorrect STORE sample classification. Additionally remove INTEL_HYBRID_LAT_CONSTRAINT() since no one uses it anymore.

Fixes 65fd435095bb ("perf/x86/intel: Update event constraints for PTL")
Cc <[email protected]> # v7.2+
---
 arch/x86/events/intel/ds.c   | 4 ++--
 arch/x86/events/perf_event.h | 4 ----
 2 files changed, 2 insertions(+), 6 deletions(-)
arch/x86/events/intel/ds.c +2 -2
index fa7e0268b4f2..ea5b331c58d1 100644
@@ -1315,8 +1315,8 @@ struct event_constraint intel_cmt_pebs_event_constraints[] = {
 
 struct event_constraint intel_dkt_pebs_event_constraints[] = {
 	/* Allow all events as PEBS with no flags */
-	INTEL_HYBRID_LAT_CONSTRAINT(0x5d0, 0xff),
-	INTEL_HYBRID_LAT_CONSTRAINT(0x6d0, 0xff),
+	INTEL_HYBRID_LDLAT_CONSTRAINT(0x5d0, 0xff),
+	INTEL_HYBRID_STLAT_CONSTRAINT(0x6d0, 0xff),
 	EVENT_CONSTRAINT_END
 };
 
arch/x86/events/perf_event.h +0 -4
index 4680cba91340..fab9da78a5c7 100644
@@ -517,10 +517,6 @@ struct cpu_hw_events {
 	__EVENT_CONSTRAINT(c, n, INTEL_ARCH_EVENT_MASK|X86_ALL_EVENT_FLAGS, \
 			  HWEIGHT(n), 0, PERF_X86_EVENT_PEBS_ST)
 
-#define INTEL_HYBRID_LAT_CONSTRAINT(c, n)	\
-	__EVENT_CONSTRAINT(c, n, INTEL_ARCH_EVENT_MASK|X86_ALL_EVENT_FLAGS, \
-			  HWEIGHT(n), 0, PERF_X86_EVENT_PEBS_LAT_HYBRID)
-
 #define INTEL_HYBRID_LDLAT_CONSTRAINT(c, n)	\
 	__EVENT_CONSTRAINT(c, n, INTEL_ARCH_EVENT_MASK|X86_ALL_EVENT_FLAGS, \
 			  HWEIGHT(n), 0, PERF_X86_EVENT_PEBS_LAT_HYBRID|PERF_X86_EVENT_PEBS_LD_HSW)
Signature · 1 line
2.55.0
author maintainer
#367 lore raw

PATCH 367/457 perf/x86/intel: Fix Panther Cove PEBS data-source snoop states

In reply to Greg Kroah-Hartman

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

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

From: Dapeng Mi <[email protected]>

commit 0ac5d6ca2c3b7d047963b67dccef17902dc6c017 upstream.

For Panther Cove, the snoop states for the data source encodings "Prefetch Promotion" and "Cross Core Prefetch Promotion" should be SNOOP_NONE instead of SNOOP_MISS.

Fix the incorrect snooping states for Panther Cove.

Fixes d2bdcde9626c ("perf/x86/intel: Add support for PEBS memory auxiliary info field in DMR")
Cc <[email protected]> # v7.0+
---
 arch/x86/events/intel/ds.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
arch/x86/events/intel/ds.c +2 -2
index 72179bcf6d0d..d0329a7eb8a5 100644
@@ -277,8 +277,8 @@ static u64 pnc_pebs_l2_hit_data_source[PNC_PEBS_DATA_SOURCE_MAX] = {
 	0,							/* 0x06: Reserved */
 	OP_LH | P(LVL, L2)  | LEVEL(L2) | P(SNOOP, HIT),	/* 0x07: L2 Hit Snoop HIT */
 	OP_LH | P(LVL, L2)  | LEVEL(L2) | P(SNOOP, HITM),	/* 0x08: L2 Hit Snoop Hit Modified */
-	OP_LH | P(LVL, L2)  | LEVEL(L2) | P(SNOOP, MISS),	/* 0x09: Prefetch Promotion */
-	OP_LH | P(LVL, L2)  | LEVEL(L2) | P(SNOOP, MISS),	/* 0x0a: Cross Core Prefetch Promotion */
+	OP_LH | P(LVL, L2)  | LEVEL(L2) | P(SNOOP, NONE),	/* 0x09: Prefetch Promotion */
+	OP_LH | P(LVL, L2)  | LEVEL(L2) | P(SNOOP, NONE),	/* 0x0a: Cross Core Prefetch Promotion */
 	0,							/* 0x0b: Reserved */
 	0,							/* 0x0c: Reserved */
 	0,							/* 0x0d: Reserved */
Signature · 1 line
2.55.0
author maintainer
#368 lore raw

PATCH 368/457 perf/x86/intel: Remove incorrect LionCove PEBS data-source constraints

In reply to Greg Kroah-Hartman

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

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

From: Dapeng Mi <[email protected]>

commit 7c944595cc43a664019edc22505b6d6f6039be5e upstream.

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
@@ -573,7 +573,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;
@@ -1516,24 +1520,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
#369 lore raw

PATCH 369/457 perf/x86/intel: Remove incorrect Panther Cove PEBS data-source constraints

In reply to Greg Kroah-Hartman

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

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

From: Dapeng Mi <[email protected]>

commit 9f93d33ad65af9853974c1ec4ba1f937e8ba1376 upstream.

Same issue exists on Panther 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_pnc_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 Pather Cove PEBS constraint table so matching falls back to the regular non-PEBS constraints, which already provide the same counter constraints.

Also update pnc_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 |   18 +++++-------------
 1 file changed, 5 insertions(+), 13 deletions(-)
arch/x86/events/intel/ds.c +5 -13
@@ -635,7 +635,11 @@ u64 pnc_latency_data(struct perf_event *
 		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;
@@ -1537,18 +1541,6 @@ struct event_constraint intel_pnc_pebs_e
 
 	INTEL_HYBRID_LDLAT_CONSTRAINT(0x1cd, 0xfc),
 	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_EVENT_CONSTRAINT_DATALA_LD_RANGE(0xd1, 0xd4, 0xf),
-
-	INTEL_FLAGS_EVENT_CONSTRAINT(0xd0, 0xf),
-	INTEL_FLAGS_EVENT_CONSTRAINT(0xd6, 0xf),
 
 	/*
 	 * Everything else is handled by PMU_FL_PEBS_ALL, because we
author maintainer
#370 lore raw

PATCH 370/457 perf/x86/intel: Constrain Panther Cove UOPS_DISPATCHED events to PMCs 0-3

In reply to Greg Kroah-Hartman

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

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

From: Dapeng Mi <[email protected]>

commit 04a7ef3b7aa3af34202285043e3423a2e3983595 upstream.

Per the latest Panther Cove event definitions, the following events are only supported on PMCs 0-3:

- UOPS_DISPATCHED.INT_EU_ALL (0x1b2)
- UOPS_DISPATCHED.ALU (0x2b2)

Add explicit event constraints for these two events so scheduling does not place them on unsupported counters.

---
 arch/x86/events/intel/core.c | 2 ++
 1 file changed, 2 insertions(+)
arch/x86/events/intel/core.c +2 -0
index a7dbbed8f94e..ecf6d6325dfe 100644
@@ -506,6 +506,8 @@ static struct event_constraint intel_pnc_event_constraints[] = {
 	INTEL_EVENT_CONSTRAINT(0xce, 0x1),
 
 	INTEL_UEVENT_CONSTRAINT(0x01b1, 0x8),
+	INTEL_UEVENT_CONSTRAINT(0x01b2, 0xf),
+	INTEL_UEVENT_CONSTRAINT(0x02b2, 0xf),
 	INTEL_UEVENT_CONSTRAINT(0x0847, 0xf),
 	INTEL_UEVENT_CONSTRAINT(0x0446, 0xf),
 	INTEL_UEVENT_CONSTRAINT(0x0846, 0xf),
Signature · 1 line
2.55.0
author maintainer
#371 lore raw

PATCH 371/457 pinctrl: mpfs-mssio: fix width of unused bank voltage setting

In reply to Greg Kroah-Hartman

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

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

From: Conor Dooley <[email protected]>

commit e6c5d6c589764a41218cbd81bd7d6c9b906e2cf0 upstream.

The bank voltages are only 4 bits wide, so when a pin was unused the driver was not correctly interpreting it as being at zero volts, because the driver's value for unused had two extra set bits.

Fixes 488d704ed7b7 ("pinctrl: add polarfire soc mssio pinctrl driver")
---
 drivers/pinctrl/microchip/pinctrl-mpfs-mssio.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/pinctrl/microchip/pinctrl-mpfs-mssio.c +1 -1
index ea1026a0d22c..dafca82f3e54 100644
@@ -86,7 +86,7 @@ static struct mpfs_pinctrl_bank_voltage mpfs_pinctrl_bank_voltages[8] = {
 	{ .uv = 1800000, .val = 4 },
 	{ .uv = 2500000, .val = 6 },
 	{ .uv = 3300000, .val = 8 },
-	{ .uv = 0, .val = 0x3f }, // pin unused
+	{ .uv = 0, .val = 0xf }, // pin unused
 };
 
 static int mpfs_pinctrl_get_drive_strength_ma(u32 drive_strength)
Signature · 1 line
2.55.0
author maintainer
#372 lore raw

PATCH 372/457 pinctrl: mpfs-mssio: use correct regmap function to set bank voltage

In reply to Greg Kroah-Hartman

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

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

From: Conor Dooley <[email protected]>

commit 8039b75af5808572ff3656eaed07d348aed3989a upstream.

regmap_assign_bits() is not the correct function to use for an RMW operation, as it maps to regmap_set_bits() or regmap_clear_bits() and the former will never zero a bit. Use regmap_update_bits() instead, which will actually set the bank voltages to what have been requested.

Fixes 488d704ed7b7 ("pinctrl: add polarfire soc mssio pinctrl driver")
---
 drivers/pinctrl/microchip/pinctrl-mpfs-mssio.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/pinctrl/microchip/pinctrl-mpfs-mssio.c +2 -2
index dafca82f3e54..92d38e5336de 100644
@@ -156,10 +156,10 @@ static void mpfs_pinctrl_set_bank_voltage(struct mpfs_pinctrl *pctrl, unsigned i
 	u32 val = FIELD_PREP(MPFS_PINCTRL_BANK_VOLTAGE_MASK, bank_voltage);
 
 	if (pin < MPFS_PINCTRL_BANK2_START)
-		regmap_assign_bits(pctrl->sysreg_regmap, MPFS_PINCTRL_MSSIO_BANK4_CFG_CR,
+		regmap_update_bits(pctrl->sysreg_regmap, MPFS_PINCTRL_MSSIO_BANK4_CFG_CR,
 				   MPFS_PINCTRL_BANK_VOLTAGE_MASK, val);
 	else
-		regmap_assign_bits(pctrl->sysreg_regmap, MPFS_PINCTRL_MSSIO_BANK2_CFG_CR,
+		regmap_update_bits(pctrl->sysreg_regmap, MPFS_PINCTRL_MSSIO_BANK2_CFG_CR,
 				   MPFS_PINCTRL_BANK_VOLTAGE_MASK, val);
 }
 
Signature · 1 line
2.55.0
author maintainer
#373 lore raw

PATCH 373/457 pinctrl: qcom: nord: Split QUP1 SE2/SE3 into lane-pair functions

In reply to Greg Kroah-Hartman

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

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

From: Shawn Guo <[email protected]>

commit b0be01f133aa36d2fd12d71c916cb8a47826b4ed upstream.

QUP1 SE2 and SE3 pack all four of their lanes pair-wise onto only two pins each: lanes 0/1 (I2C SDA/SCL) at mux value 2 and lanes 2/3 (UART TX/RX) at mux value 1, on gpio127/gpio128 and gpio129/gpio130 respectively.

Both mux values were named "qup1_se2" (respectively "qup1_se3"), so the two distinct lane pairs became indistinguishable. msm_pinmux_set_mux() stops at the first entry matching the requested function, which means mux value 1 was always selected and the I2C lanes could never be muxed out. In practice i2c9 and i2c10 got the UART lanes and did not work, while uart9 and uart10 happened to be muxed correctly.

Give each lane pair its own function, following the _01/_23 naming already used for the same hardware arrangement by the shikra, eliza, hawi and maili TLMM drivers. Both functions still cover the full pin pair, so a single pinctrl state per protocol remains sufficient.

Drop gpio129/gpio130 from the SE2 group list, since those pins belong to SE3 and were never reachable through the SE2 function.

Also rename QUP1 SE2/SE3 functions in the binding doc accordingly.

While at it, add missing "gpio", "qup3_se0_mira" and "qup3_se0_mirb" to the binding function enum to get the list complete.

Fixes c24dd0826f06 ("pinctrl: qcom: add the TLMM driver for the Nord platforms")
Assisted-by LLM
---
 .../bindings/pinctrl/qcom,nord-tlmm.yaml      |  7 ++--
 drivers/pinctrl/qcom/pinctrl-nord.c           | 34 +++++++++++++------
 2 files changed, 27 insertions(+), 14 deletions(-)
Documentation/devicetree/bindings/pinctrl/qcom,nord-tlmm.yaml +4 -3
index 4bb511719f31..56758a85ead8 100644
@@ -67,7 +67,7 @@ $defs:
           Specify the alternative function to be configured for the specified
           pins.
 
-        enum: [ aoss_cti, atest_char, atest_usb20, atest_usb21,
+        enum: [ gpio, aoss_cti, atest_char, atest_usb20, atest_usb21,
                 aud_intfc0_clk, aud_intfc0_data, aud_intfc0_ws,
                 aud_intfc10_clk, aud_intfc10_data, aud_intfc10_ws,
                 aud_intfc1_clk, aud_intfc1_data, aud_intfc1_ws,
@@ -98,9 +98,10 @@ $defs:
                 pcie3_clk_req_n, phase_flag, pll_bist_sync, pll_clk_aux,
                 prng_rosc0, prng_rosc1, pwrbrk_i_n, qdss, qdss_cti, qspi,
                 qup0_se0, qup0_se1, qup0_se2, qup0_se3, qup0_se4, qup0_se5,
-                qup1_se0, qup1_se1, qup1_se3, qup1_se2, qup1_se4, qup1_se5,
+                qup1_se0, qup1_se1, qup1_se2_01, qup1_se2_23, qup1_se3_01,
+                qup1_se3_23, qup1_se4, qup1_se5,
                 qup1_se6, qup2_se0, qup2_se1, qup2_se2, qup2_se3, qup2_se4,
-                qup2_se5, qup2_se6,
+                qup2_se5, qup2_se6, qup3_se0_mira, qup3_se0_mirb,
                 sailss_ospi, sdc4_clk, sdc4_cmd, sdc4_data, smb_alert,
                 smb_alert_n, smb_clk, smb_dat, tb_trig_sdc4, tmess_prng0,
                 tmess_prng1, tsc_timer, tsense_pwm, usb0_hs,
drivers/pinctrl/qcom/pinctrl-nord.c +23 -11
index 7c21306e77ff..7f37f8e819ba 100644
@@ -570,8 +570,10 @@ enum nord_functions {
 	msm_mux_qup0_se5,
 	msm_mux_qup1_se0,
 	msm_mux_qup1_se1,
-	msm_mux_qup1_se2,
-	msm_mux_qup1_se3,
+	msm_mux_qup1_se2_01,
+	msm_mux_qup1_se2_23,
+	msm_mux_qup1_se3_01,
+	msm_mux_qup1_se3_23,
 	msm_mux_qup1_se4,
 	msm_mux_qup1_se5,
 	msm_mux_qup1_se6,
@@ -1152,11 +1154,19 @@ static const char *const qup1_se1_groups[] = {
 	"gpio123", "gpio124", "gpio125", "gpio126",
 };
 
-static const char *const qup1_se2_groups[] = {
-	"gpio127", "gpio128", "gpio129", "gpio130",
+static const char *const qup1_se2_01_groups[] = {
+	"gpio127", "gpio128",
 };
 
-static const char *const qup1_se3_groups[] = {
+static const char *const qup1_se2_23_groups[] = {
+	"gpio127", "gpio128",
+};
+
+static const char *const qup1_se3_01_groups[] = {
+	"gpio129", "gpio130",
+};
+
+static const char *const qup1_se3_23_groups[] = {
 	"gpio129", "gpio130",
 };
 
@@ -1428,8 +1438,10 @@ static const struct pinfunction nord_functions[] = {
 	MSM_PIN_FUNCTION(qup0_se5),
 	MSM_PIN_FUNCTION(qup1_se0),
 	MSM_PIN_FUNCTION(qup1_se1),
-	MSM_PIN_FUNCTION(qup1_se2),
-	MSM_PIN_FUNCTION(qup1_se3),
+	MSM_PIN_FUNCTION(qup1_se2_01),
+	MSM_PIN_FUNCTION(qup1_se2_23),
+	MSM_PIN_FUNCTION(qup1_se3_01),
+	MSM_PIN_FUNCTION(qup1_se3_23),
 	MSM_PIN_FUNCTION(qup1_se4),
 	MSM_PIN_FUNCTION(qup1_se5),
 	MSM_PIN_FUNCTION(qup1_se6),
@@ -1633,13 +1645,13 @@ static const struct msm_pingroup nord_groups[] = {
 			 _, _, _, _, _, _, _),
 	[126] = PINGROUP(126, qup1_se1, qup1_se0, ccu_i2c_scl, mdp1_vsync_out,
 			 _, atest_usb20, ddr_pxi, _, _, _, _),
-	[127] = PINGROUP(127, qup1_se2, qup1_se2, _, atest_usb21, ddr_pxi,
+	[127] = PINGROUP(127, qup1_se2_23, qup1_se2_01, _, atest_usb21, ddr_pxi,
 			 _, _, _, _, _, _),
-	[128] = PINGROUP(128, qup1_se2, qup1_se2, _, atest_usb20, ddr_pxi,
+	[128] = PINGROUP(128, qup1_se2_23, qup1_se2_01, _, atest_usb20, ddr_pxi,
 			 _, _, _, _, _, _),
-	[129] = PINGROUP(129, qup1_se3, qup1_se3, ccu_i2c_sda, mdp1_vsync_out,
+	[129] = PINGROUP(129, qup1_se3_23, qup1_se3_01, ccu_i2c_sda, mdp1_vsync_out,
 			 _, atest_usb21, ddr_pxi, _, _, _, _),
-	[130] = PINGROUP(130, qup1_se3, qup1_se3, ccu_i2c_scl, mdp1_vsync_out,
+	[130] = PINGROUP(130, qup1_se3_23, qup1_se3_01, ccu_i2c_scl, mdp1_vsync_out,
 			 _, atest_usb20, ddr_pxi, _, _, _, _),
 	[131] = PINGROUP(131, qup1_se4, qup1_se6, ccu_i2c_sda, mdp1_vsync_out,
 			 _, atest_usb21, ddr_pxi, _, _, _, _),
Signature · 1 line
2.55.0
author maintainer
#374 lore raw

PATCH 374/457 pinctrl: single: free the IRQ on domain creation failure

In reply to Greg Kroah-Hartman

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

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

From: Myeonghun Pak <[email protected]>

commit 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
@@ -1628,7 +1628,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
#375 lore raw

PATCH 375/457 pinctrl: sunxi: keep a shadow copy of the data register output latches

In reply to Greg Kroah-Hartman

7.2-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
@@ -843,6 +843,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);
 }
 
@@ -1023,21 +1038,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);
 
@@ -1578,7 +1601,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);
@@ -1616,6 +1639,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);
@@ -1671,7 +1725,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;
@@ -1705,13 +1758,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 SUN4I_FUNC_DISABLED_OLD 7
 #define SUN4I_FUNC_DISABLED_NEW 15
@@ -177,6 +178,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			flags;
 	u32				bank_mem_size;
author maintainer
#376 lore raw

PATCH 376/457 s390/cio: Fix NULL pointer dereference in ccw_device_get_util_str()

In reply to Greg Kroah-Hartman

7.2-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
#377 lore raw

PATCH 377/457 s390/cmf: Fix virtual vs physical address confusion

In reply to Greg Kroah-Hartman

7.2-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
@@ -183,7 +183,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
#378 lore raw

PATCH 378/457 s390/pci: Fix leak of struct pci_dev reference in zpci_report_status()

In reply to Greg Kroah-Hartman

7.2-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
@@ -257,7 +257,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
@@ -89,7 +89,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
  *
@@ -103,15 +104,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 */
@@ -121,8 +122,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
#379 lore raw

PATCH 379/457 s390/pci: Fix missing device lock in zpci_report_status()

In reply to Greg Kroah-Hartman

7.2-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
@@ -256,8 +256,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
@@ -87,6 +87,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
@@ -108,7 +121,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;
 
@@ -122,16 +134,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
#380 lore raw

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

In reply to Greg Kroah-Hartman

7.2-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
@@ -320,8 +320,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
#381 lore raw

PATCH 381/457 s390/pci: Dont report recovery success on skipped recovery

In reply to Greg Kroah-Hartman

7.2-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
@@ -190,6 +190,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
#382 lore raw

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

In reply to Greg Kroah-Hartman

7.2-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
#383 lore raw

PATCH 383/457 KVM: Ensure memory attributes xarray nodes are accounted to the callers memcg

In reply to Greg Kroah-Hartman

7.2-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
@@ -1116,7 +1116,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
#384 lore raw

PATCH 384/457 KVM: Dont treat reserved xarray entries as having memory attributes

In reply to Greg Kroah-Hartman

7.2-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
@@ -2446,14 +2446,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
#385 lore raw

PATCH 385/457 KVM: SEV: Free have_run_cpus during VM destruction even if VM is no longer SEV

In reply to Greg Kroah-Hartman

7.2-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
@@ -2981,13 +2981,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
#386 lore raw

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

In reply to Greg Kroah-Hartman

7.2-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
@@ -2048,6 +2048,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);
@@ -2187,6 +2193,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
#387 lore raw

PATCH 387/457 KVM: arm64: Dont WARN on an unknown VM ioctl in protected mode

In reply to Greg Kroah-Hartman

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

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

From: Fuad Tabba <[email protected]>

commit 49d9d295d69d07e850cae35933ba8519e2915f26 upstream.

kvm_pkvm_ioctl_allowed() WARNs when kvm_get_cap_for_kvm_ioctl() doesn't find the ioctl number in vm_ioctl_caps[], and kvm_arch_vm_ioctl() calls it for every number the generic code doesn't handle, so ioctl(vm_fd, 0xdeadbeef) from userspace taints a pKVM host and panics it under panic_on_warn. The lookup is fed userspace input: return false, and userspace gets the -EINVAL kvm_arch_vm_ioctl() returns for that number on a host without pKVM.

Fixes b12b3b04f6ba0 ("KVM: arm64: Check whether a VM IOCTL is allowed in pKVM")
---
 arch/arm64/include/asm/kvm_pkvm.h |    3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)
arch/arm64/include/asm/kvm_pkvm.h +1 -2
@@ -62,8 +62,7 @@ static inline bool kvm_pkvm_ioctl_allowe
 	int r;
 
 	r = kvm_get_cap_for_kvm_ioctl(ioctl, &ext);
-
-	if (WARN_ON_ONCE(r < 0))
+	if (r < 0)
 		return false;
 
 	return kvm_pkvm_ext_allowed(kvm, ext);
author maintainer
#388 lore raw

PATCH 388/457 KVM: arm64: Fix AArch32 DBGBXVR<n> handling

In reply to Greg Kroah-Hartman

7.2-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
@@ -889,6 +889,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
#389 lore raw

PATCH 389/457 KVM: arm64: Fix spurious warning for benign stage 2 teardown race

In reply to Greg Kroah-Hartman

7.2-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
#390 lore raw

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

In reply to Greg Kroah-Hartman

7.2-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
@@ -1256,6 +1256,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;
@@ -1272,7 +1283,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)
@@ -1291,7 +1302,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
#391 lore raw

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

In reply to Greg Kroah-Hartman

7.2-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
#392 lore raw

PATCH 392/457 RISC-V: KVM: Serialize IMSIC attributes with vCPU migration

In reply to Greg Kroah-Hartman

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

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

From: Xie Bo <[email protected]>

commit 8ae12ccaec6ec74945d8c1ef39f2c1b8df779abc upstream.

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
@@ -969,9 +969,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);
@@ -995,6 +1000,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
#393 lore raw

PATCH 393/457 RISC-V: KVM: Synchronize hrtimer callback during teardown

In reply to Greg Kroah-Hartman

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

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

From: Myeonghun Pak <[email protected]>

commit 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
@@ -61,10 +61,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
#394 lore raw

PATCH 394/457 RISC-V: KVM: Release unused page after MMU invalidation

In reply to Greg Kroah-Hartman

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

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

From: Xie Bo <[email protected]>

commit ed54fdb460a6e81d5f8f38388d1f10b385dd4a58 upstream.

If an MMU invalidation races with a G-stage fault, the fault handler skips installing the page but leaves ret set to zero. As a result, kvm_release_faultin_page() treats the page as used and can unnecessarily mark it dirty.

Track the invalidation retry separately and release the page as unused, while preserving the existing return value so that the vCPU retries the fault.

Fixes 2ed90cb0938a ("KVM: RISC-V: Retry fault if vma_lookup() results become invalid")
---
 arch/riscv/kvm/mmu.c |    8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)
arch/riscv/kvm/mmu.c +6 -2
@@ -540,6 +540,7 @@ int kvm_riscv_mmu_map(struct kvm_vcpu *v
 	int ret;
 	kvm_pfn_t hfn;
 	bool is_hugetlb;
+	bool unused = false;
 	bool writable;
 	short vma_pageshift;
 	gfn_t gfn = gpa >> PAGE_SHIFT;
@@ -632,8 +633,10 @@ int kvm_riscv_mmu_map(struct kvm_vcpu *v
 
 	write_lock(&kvm->mmu_lock);
 
-	if (mmu_invalidate_retry(kvm, mmu_seq))
+	if (mmu_invalidate_retry(kvm, mmu_seq)) {
+		unused = true;
 		goto out_unlock;
+	}
 
 	/*
 	 * Check if we are backed by a THP and thus use block mapping if
@@ -656,7 +659,8 @@ int kvm_riscv_mmu_map(struct kvm_vcpu *v
 		kvm_err("Failed to map in G-stage\n");
 
 out_unlock:
-	kvm_release_faultin_page(kvm, page, ret && ret != -EEXIST, writable);
+	kvm_release_faultin_page(kvm, page,
+				 unused || (ret && ret != -EEXIST), writable);
 	write_unlock(&kvm->mmu_lock);
 	return ret;
 }
author maintainer
#395 lore raw

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

In reply to Greg Kroah-Hartman

7.2-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
@@ -621,6 +621,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
@@ -267,7 +267,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
#396 lore raw

PATCH 396/457 RISC-V: KVM: Fix HSM hart status error propagation

In reply to Greg Kroah-Hartman

7.2-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
#397 lore raw

PATCH 397/457 sched_ext: Derive SCX_RQ_IN_WAKEUP from the core enqueue flags

In reply to Greg Kroah-Hartman

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

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

From: Tejun Heo <[email protected]>

commit df5cdc2c832ca4e8a6d774596b9005558761a403 upstream.

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")
---
 kernel/sched/ext/ext.c |    7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)
kernel/sched/ext/ext.c +6 -1
@@ -2037,7 +2037,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;
 
 	/*
author maintainer
#398 lore raw

PATCH 398/457 sched/cache: Decouple sched_cache_group from mm to fix UAF

In reply to Greg Kroah-Hartman

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

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

From: Tim Chen <[email protected]>

commit 28f9c0e0a0b94c5d3e1b634db545f6e1f94858c5 upstream.

Currently the sched cache grouping is by mm and the scheduling statistics sched_cache_stat lives in the mm structure. This ties the life cycle of scheduling stats with mm.

In account_mm_sched(), the scheduling stats are accessed by task->mm->sc_stat. However, a task may be switching mm on one CPU when another CPU is running account_mm_sched(), and possibly accessing the old mm that was freed. This problem was found when running tests with KASAN by Hyunwoo:

https://lore.kernel.org/lkml/apPb-Dr4nPYuHQOK@v4bel/

Instead of serializing the mm access by introducing extra acquisition of rq lock in the mm free path, extract sched_cache_stat from mm_struct, rename it as sched_cache_group and manage its life cycle apart from mm_struct with its own ref counting. This allows us in the next patch access sched_cache_group directly from task, and add a refcount on sched_cache_group when a task links to it. This prevents the use after free issue when accessing stale and released old mm and its sched cache stat a task switches to a new mm while account_mm_sched() is done elsewhere.

The other benefit of this restructure is in the future, the grouping of tasks to a LLC would have the flexibility to be associated with a user defined grouping, or cgroup, cookie group, numa_group or others instead of just with a single mm address space.

Rename sched_cache_stat to sched_cache_group and turn it into a refcounted object allocated from mm_struct. The mm_struct now holds a pointer (sched_cache_grp) to this object instead of embedding it.

---
 include/linux/mm_types.h |   15 +---
 include/linux/sched.h    |    6 +
 kernel/exit.c            |   11 ++
 kernel/sched/fair.c      |  173 ++++++++++++++++++++++++++++++++++-------------
 4 files changed, 144 insertions(+), 61 deletions(-)
include/linux/mm_types.h +5 -10
@@ -1211,7 +1211,7 @@ struct mm_struct {
 		struct mm_mm_cid mm_cid;
 
 		/* sched_cache related statistics */
-		struct sched_cache_stat sc_stat;
+		struct sched_cache_group *sched_cache_grp;
 #ifdef CONFIG_MMU
 		atomic_long_t pgtables_bytes;	/* size of all page tables */
 #endif
@@ -1609,8 +1609,9 @@ static inline unsigned int mm_cid_size(v
 #endif /* CONFIG_SCHED_MM_CID */
 
 #ifdef CONFIG_SCHED_CACHE
-void mm_init_sched(struct mm_struct *mm,
-		   struct sched_cache_time __percpu *pcpu_sched);
+int mm_init_sched(struct mm_struct *mm,
+		  struct sched_cache_time __percpu *pcpu_sched);
+void mm_destroy_sched(struct mm_struct *mm);
 
 static inline int mm_alloc_sched_noprof(struct mm_struct *mm)
 {
@@ -1620,17 +1621,11 @@ static inline int mm_alloc_sched_noprof(
 	if (!pcpu_sched)
 		return -ENOMEM;
 
-	mm_init_sched(mm, pcpu_sched);
-	return 0;
+	return mm_init_sched(mm, pcpu_sched);
 }
 
 #define mm_alloc_sched(...)	alloc_hooks(mm_alloc_sched_noprof(__VA_ARGS__))
 
-static inline void mm_destroy_sched(struct mm_struct *mm)
-{
-	free_percpu(mm->sc_stat.pcpu_sched);
-	mm->sc_stat.pcpu_sched = NULL;
-}
 #else /* !CONFIG_SCHED_CACHE */
 
 static inline int mm_alloc_sched(struct mm_struct *mm) { return 0; }
include/linux/sched.h +4 -2
@@ -2398,7 +2398,7 @@ struct sched_cache_time {
 	unsigned long epoch;
 };
 
-struct sched_cache_stat {
+struct sched_cache_group {
 	struct sched_cache_time __percpu *pcpu_sched;
 	raw_spinlock_t lock;
 	unsigned long epoch;
@@ -2406,11 +2406,13 @@ struct sched_cache_stat {
 	unsigned long next_scan;
 	unsigned long footprint;
 	int cpu;
+	refcount_t refcnt;
+	struct rcu_head rcu;
 } ____cacheline_aligned_in_smp;
 
 #else
 
-struct sched_cache_stat { };
+struct sched_cache_group { };
 
 #endif
 
kernel/exit.c +8 -3
@@ -559,18 +559,23 @@ void mm_update_next_owner(struct mm_stru
  */
 static void exit_mm_sched_cache(struct mm_struct *mm)
 {
+	struct sched_cache_group *grp;
 	unsigned long fp, sub;
 
 	if (!current->total_numa_faults)
 		return;
 	/*
 	 * No lock protection due to performance considerations.
-	 * Make sure mm->sc_stat.footprint does not become
+	 * Make sure the group footprint does not become
 	 * negative.
 	 */
-	fp = READ_ONCE(mm->sc_stat.footprint);
+	grp = READ_ONCE(mm->sched_cache_grp);
+	if (!grp)
+		return;
+
+	fp = READ_ONCE(grp->footprint);
 	sub = min(fp, current->total_numa_faults);
-	WRITE_ONCE(mm->sc_stat.footprint, fp - sub);
+	WRITE_ONCE(grp->footprint, fp - sub);
 }
 #else
 static inline void exit_mm_sched_cache(struct mm_struct *mm)
kernel/sched/fair.c +127 -46

367 lines not on this page · raw message

author maintainer
#399 lore raw

PATCH 399/457 sched/cache: Refresh LLC capacity across CPU hotplug, to fix capacity underestimation bug

In reply to Greg Kroah-Hartman

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

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

From: Davi Chaves Azevedo <[email protected]>

commit 3cb0243767fd033bdce95f4f1b5882172a2f8119 upstream.

The scheduler scales LLC capacity by the fraction of cache-sharing CPUs covered by a domain:

llc_bytes = cache_size * span_weight / shared_weight

During CPU teardown, sched_cpu_deactivate() rebuilds scheduler domains before cacheinfo_cpu_pre_down() removes the CPU from shared_cpu_map. The new domains therefore use the old sharing weight. The later call to sched_update_llc_bytes() looks up the departing CPU's sd_llc, which has already been detached, and returns without correcting the surviving CPUs.

On a Ryzen 5 7535U with twelve logical CPUs sharing a 16 MiB LLC, offlining one SMT sibling left the remaining CPUs with:

llc_bytes = floor(16777216 * 11 / 12) = 15379114 bytes

The correct capacity is still 16777216 bytes. On systems with active cache-aware scheduling, an underestimated capacity can cause exceed_llc_capacity() to reject aggregation for a process whose footprint would fit. Unchanged cpuset partitions sharing the physical cache can also retain stale capacity when a CPU comes online in another partition.

Pass the cache-sharing mask already retained by cacheinfo to the scheduler update. Refresh every surviving CPU using its own LLC domain so that each partition receives the correct share. This also preserves the correction needed as cache-sharing maps grow during boot.

Keep the existing CPU-hotplug and scheduler-domain synchronization. The update remains on the hotplug path; no steady-state scheduling operation or persistent allocation is added.

---
 drivers/base/cacheinfo.c       | 11 ++++++-----
 include/linux/sched/topology.h |  4 ++--
 kernel/sched/topology.c        | 22 +++++++++++++---------
 3 files changed, 21 insertions(+), 16 deletions(-)
drivers/base/cacheinfo.c +6 -5
index 9f9c72727a05..7a47a392568a 100644
@@ -1040,9 +1040,10 @@ static int cacheinfo_cpu_online(unsigned int cpu)
 	rc = cache_add_dev(cpu);
 	if (rc)
 		goto err;
-	if (cpu_map_shared_cache(true, cpu, &cpu_map))
+	if (cpu_map_shared_cache(true, cpu, &cpu_map)) {
 		update_per_cpu_data_slice_size(true, cpu, cpu_map);
-	sched_update_llc_bytes(cpu);
+		sched_update_llc_bytes(cpu_map);
+	}
 	return 0;
 err:
 	free_cache_attributes(cpu);
@@ -1059,10 +1060,10 @@ static int cacheinfo_cpu_pre_down(unsigned int cpu)
 		cpu_cache_sysfs_exit(cpu);
 
 	free_cache_attributes(cpu);
-	if (nr_shared > 1)
+	if (nr_shared > 1) {
 		update_per_cpu_data_slice_size(false, cpu, cpu_map);
-
-	sched_update_llc_bytes(cpu);
+		sched_update_llc_bytes(cpu_map);
+	}
 
 	return 0;
 }
include/linux/sched/topology.h +2 -2
index b5d9d7c2b8ad..f96812d71c51 100644
@@ -281,9 +281,9 @@ static inline int task_node(const struct task_struct *p)
 }
 
 #ifdef CONFIG_SCHED_CACHE
-extern void sched_update_llc_bytes(unsigned int cpu);
+extern void sched_update_llc_bytes(const struct cpumask *cpus);
 #else
-static inline void sched_update_llc_bytes(unsigned int cpu) { }
+static inline void sched_update_llc_bytes(const struct cpumask *cpus) { }
 #endif
 
 #endif /* _LINUX_SCHED_TOPOLOGY_H */
kernel/sched/topology.c +13 -9
index 0248227d983a..3dab0253976f 100644
@@ -985,8 +985,8 @@ void sched_cache_active_set(void)
 }
 
 /*
- * Update the bottom sched_domain's llc_bytes for @cpu and all its
- * LLC siblings. Called from cacheinfo_cpu_online() or
+ * Update the bottom sched_domain's llc_bytes for @cpus sharing a physical
+ * LLC. Called from cacheinfo_cpu_online() or
  * cacheinfo_cpu_pre_down() with cpu hotplug lock held.
  *
  * Note: get_effective_llc_bytes() returns 0 on PowerPC.
@@ -996,17 +996,13 @@ void sched_cache_active_set(void)
  * and does not populates the per-CPU struct cpu_cacheinfo array
  * that get_cpu_cacheinfo_llc() reads.
  */
-void sched_update_llc_bytes(unsigned int cpu)
+void sched_update_llc_bytes(const struct cpumask *cpus)
 {
 	struct sched_domain *sd, *sdp;
 	unsigned int i;
 
 	sched_domains_mutex_lock();
 
-	sdp = rcu_dereference_sched_domain(per_cpu(sd_llc, cpu));
-	if (!sdp)
-		goto unlock;
-
 	/*
 	 * ci->shared_cpu_map is built incrementally as CPUs come
 	 * online, so the first CPU in an LLC initially sees
@@ -1014,14 +1010,22 @@ void sched_update_llc_bytes(unsigned int cpu)
 	 * get_effective_llc_bytes().  Re-evaluating every LLC
 	 * sibling on each online event corrects this once the full
 	 * shared_cpu_map is known.
+	 *
+	 * The departing CPU's domains have already been detached when
+	 * cacheinfo removes it. Use the surviving cache siblings instead.
+	 * They may belong to different cpuset partitions, so use each CPU's
+	 * own LLC domain to scale its share of the physical cache.
 	 */
-	for_each_cpu(i, sched_domain_span(sdp)) {
+	for_each_cpu(i, cpus) {
+		sdp = rcu_dereference_sched_domain(per_cpu(sd_llc, i));
+		if (!sdp)
+			continue;
+
 		sd = rcu_dereference_sched_domain(cpu_rq(i)->sd);
 		if (sd)
 			sd->llc_bytes = get_effective_llc_bytes(i, sdp);
 	}
 
-unlock:
 	sched_domains_mutex_unlock();
 }
 
Signature · 1 line
2.55.0
author maintainer
#400 lore raw

PATCH 400/457 Bluetooth: hci_conn: fix CIS hold ownership on reuse

In reply to Greg Kroah-Hartman

7.2-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
@@ -2079,6 +2079,8 @@ struct hci_conn *hci_bind_cis(struct hci
 		cis->conn_timeout = timeout;
 	}
 
+	hci_conn_hold(cis);
+
 	if (cis->state == BT_CONNECTED)
 		return cis;
 
@@ -2120,7 +2122,6 @@ struct hci_conn *hci_bind_cis(struct hci
 		return ERR_PTR(-EINVAL);
 	}
 
-	hci_conn_hold(cis);
 	cis->state = BT_BOUND;
 
 	return cis;
@@ -2496,6 +2497,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
#401 lore raw

PATCH 401/457 Bluetooth: hci_sock: validate event length before filtering

In reply to Greg Kroah-Hartman

7.2-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
@@ -164,6 +164,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;
@@ -177,7 +178,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;
@@ -186,11 +191,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
#402 lore raw

PATCH 402/457 Bluetooth: hci_sock: reject out-of-range OCF values

In reply to Greg Kroah-Hartman

7.2-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
@@ -1892,7 +1892,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
#403 lore raw

PATCH 403/457 Bluetooth: ISO: balance the parent hold in hci_bind_bis()

In reply to Greg Kroah-Hartman

7.2-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
@@ -2375,10 +2375,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
#404 lore raw

PATCH 404/457 Bluetooth: ISO: release unused CIS holds after channel attach

In reply to Greg Kroah-Hartman

7.2-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
@@ -496,6 +496,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);
@@ -568,8 +569,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
#405 lore raw

PATCH 405/457 Bluetooth: L2CAP: validate frame length before control and FCS access

In reply to Greg Kroah-Hartman

7.2-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
@@ -6701,9 +6701,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
#406 lore raw

PATCH 406/457 Bluetooth: mgmt: fix race in read_unconf_index_list()

In reply to Greg Kroah-Hartman

7.2-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
@@ -496,13 +496,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
#407 lore raw

PATCH 407/457 Bluetooth: RFCOMM: fix NULL dereference of dlc->session in RFCOMM_CONNINFO

In reply to Greg Kroah-Hartman

7.2-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
#408 lore raw

PATCH 408/457 smb: client: fix create context out-of-bounds reads

In reply to Greg Kroah-Hartman

7.2-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
@@ -4569,25 +4569,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
#409 lore raw

PATCH 409/457 smb: client: close handle after create-context parsing failure

In reply to Greg Kroah-Hartman

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

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

From: Zihan Xi <[email protected]>

commit 566820af017e81497fb5e9d3ad6e7ffe2828bc8b upstream.

SMB2_open() accounts a successful CREATE response as a remote open before parsing its create contexts. If smb2_parse_contexts() rejects malformed context data, SMB2_open() returns without closing the handle, leaving the server-side handle open and num_remote_opens elevated.

Close the handle after a post-CREATE context parsing failure so the error path releases the remote resource and balances the open count.

Fixes af1689a9b770 ("smb: client: fix potential OOBs in smb2_parse_contexts()")
Assisted-by LLM
---
 fs/smb/client/smb2pdu.c |    3 +++
 1 file changed, 3 insertions(+)
fs/smb/client/smb2pdu.c +3 -0
@@ -3413,6 +3413,9 @@ replay_again:
 
 	rc = smb2_parse_contexts(server, &rsp_iov, &oparms->fid->epoch,
 				 oparms->fid->lease_key, oplock, file_info, posix);
+	if (rc)
+		SMB2_close(xid, tcon, oparms->fid->persistent_fid,
+			   oparms->fid->volatile_fid);
 
 	trace_smb3_open_done(xid, rsp->PersistentFileId, tcon->tid, ses->Suid,
 			     oparms->create_options, oparms->desired_access,